'How to automatically disable a Spring bean when running a unit test?

I have a service class which starts a thread and run some background tasks. These tasks should not be run while I'm running a unit test. The service class itself is so simple that it doesn't need to be unit tested. It is like:

@Service
public class BackgroundTaskService {
    @PostConstruct
    public void startTask() {
        // ...
    }
}

Currently I'm setting a system property to declare that a unit test is running:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTest {
    static {
        System.setProperty("unit_testing", "true");
    }
    @Test
    public void test() {}
}

Then I can check:

@PostConstruct
public void startTask() {
    if (System.getProperty("unit_testing") != null) {
        return;  // skip background tasks
    }
}

I'd like to know if there is a better way to do that.



Solution 1:[1]

A prettier way to handle this would be

@Service
public class BackgroundTaskService {

    @PostConstruct
    @Profile("!test")
    public void startTask() {
        // ...
    }
}

or even

@PostConstruct
public void startTask() {
    if(env.acceptsProfiles("!test")) {  // env is @Autowired Environment
        // start task
    }
}

Only if the test profile is not active, the @PostConstruct method is run. In a Spring environment you want to use the tools Spring gives you, so use a profile instead of some custom indicator.

Solution 2:[2]

It's not a good practice to skip unit tests for even simple code, but if you really want to do that you can have a look at this thread. Instead of a property you can have a static boolean variable.

Note to community: I'm new to this forum so my reputation doesn't allow me to comment. Thanks.

Solution 3:[3]

Consider @MockBean. The presence of a @MockBean will remove the bean that is being mocked:

Any existing single bean of the same type defined in the context will be replaced by the mock.

https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html

Instead of invoking the original behavior, all the methods of the mock will just return a blank or empty value by default:

By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate.

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

This may or may not be good enough for one's use case, to replace the normal bean with a dummy bean rather than disable the bean completely.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Alexander Taylor