3

Bear with me as this is the first time I've used Spring Boot so this is only what I think is happening...

I have a couple of methods which are annotated with @Scheduled. They work great, and I have all of the dependencies configured and injected. These dependencies are quite heavy weight, relying on internet connections etc. I've annotated them as @Lazy, so they're only instantiated at the very last minute.

However, the classes which contain the scheduled methods need to be marked with @Component which means they're created on launch. This sets off a chain reaction which creates all of my dependencies whether I actually need them for the test I'm currently running.

When I run my unit tests on our CI server, they fail because the server isn't auth'd with the database (nor should it be).

The tests which test these @Scheduled jobs inject their own mocks, so they work fine. However, the tests which are completely unrelated are causing the problems as the classes are still created. I obviously don't want to create mocks in these tests for completely unrelated classes.

How can I prevent certain a @Component from being created when the tests are running?

Scheduled jobs class:

package example.scheduledtasks;

@Component
public class ScheduledJob {

    private Database database;

    @Autowired
    public AccountsImporter(Database database) {
        this.database = database;
    }

    @Scheduled(cron="0 0 04 * * *")
    public void run() {
        // Do something with the database
    }
}

Config class:

package example

@Configuration
public class ApplicationConfig {

    @Bean
    @Lazy
    public Database database() {
        return ...;// Some heavy operation I don't want to do while testing.
    }

}

Josh
  • 3,280
  • 3
  • 34
  • 52
  • Perhaps [this answer](http://stackoverflow.com/a/27333573/3776810) might help. – Jaims Sep 09 '16 at 16:16
  • Thanks @Jaims, I've seen the conditional annotations but can't figure out what my condition would be. Any ideas? – Josh Sep 09 '16 at 16:22
  • 1
    A common way is to use different profiles. You have a `TestConfig` class with a `@Profile()` annotation which could mock your Database to a light object. On your test class you could then use `@ActiveProfiles()` to select your test config. While in production, you could provide a different configuration profile. – Jaims Sep 09 '16 at 17:09

2 Answers2

5

I know you said:

I obviously don't want to create mocks in these tests for completely unrelated classes.

Still, just so you know, you can easily override the unwanted component just for this test:

@RunWith(...)
@Context...
public class YourTest {
    public static class TestConfiguration {
        @Bean
        @Primary
        public Database unwantedComponent(){
            return Mockito.mock(Database.class);
        }
    }

    @Test
    public void yourTest(){
        ...
    }
}

Similar question/answer: Override a single @Configuration class on every spring boot @Test

Community
  • 1
  • 1
alexbt
  • 14,285
  • 6
  • 67
  • 82
  • Thanks Alex. While this solves the immediate problem of the tests not passing, I still don't think it makes sense that Spring registers scheduled tasks when running tests. The tests should just be the classes I instantiated, surely?! – Josh Sep 12 '16 at 08:34
  • This doesn't remove the other bean from the context. If you want to replace a bean you need to make sure the bean's name is the same in your test configuration as it is in the spring context normally. This answer only helps if you are trying to inject a new value into a singled-valued dependency. – user988346 Aug 02 '19 at 21:25
2

Just add the following to your test class:

@MockBean
public Database d;
membersound
  • 66,525
  • 139
  • 452
  • 886