Resetting Mockito mocks, provided as Spring beans, between tests? - spring

I have a Java application that uses Spring's dependency injection. I want to mock out a bean, and verify that it receives certain method calls.
The problem is that Mockito does not reset the mock between tests, so I cannot correctly verify method calls on it.
My unit under test:
public class MyClass {
#Resource
SomeClientClass client;
public void myMethod() {
client.someMethod();
}
}
The unit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = UnitTestConfig.class)
public class MyClassTest {
#Resource
SomeClientClass client;
#Test
public void verifySomething() {
// ...
Mockito.verify(client).publish();
}
}
Finally,
#Configuration
public class UnitTestConfig {
#Bean
SomeClientClass client() {
return Mockito.mock(SomeClientClass.class);
}
}
Though I could hack my way around this problem by manually resetting mocks between tests, I wonder if there's a cleaner / more idiomatic approach.

I had to add this at the start:
#BeforeEach
void setup() {
Mockito.reset(...mockBeans);
...
}

Author not explained why he needs it, I can put more details.
Combining Spring's dependency injection with Mockito in this way not the best approach.
It leads to errors, because same Mocks will be reused between different tests!
This means that verify() will work incorrectly. It will accumulate method invocations from different tests. For example you will get "Wanted 1 time:" - "But was 2 times".
Most generic solution for this in Mockito is using #InjectMocks.
This annotation doing 2 important things:
actually injecting all #Mock fields into class annotated with #InjectMocks
resets each #Mock annotated class. (so, verify() will not accumulate invocations from different tests)
Code example:
#RunWith(MockitoJUnitRunner.class)
public class SomeSpringConverterTest {
#InjectMocks
private SomethingToJsonNodeSpringConverter someSpringConverter;
#Mock
private SomethingDatamodelService someDatamodelService;
#Test
public void convertOriginalContainerTest() {
SomethingContainer someContainer = buildSomeContainer("aa", "bb");
Mockito.when(someDatamodelService.getAttributes()).thenReturn(Arrays.asList("aa", "bb"));
JsonNode node = someSpringConverter.convert(someContainer, JsonNode.class);
Mockito.verify(someDatamodelService.getAttributes());
assertTrue(node.get("aa") != null);
}
#Test
public void convertOriginalContainerTest() {
SomethingContainer someContainer = buildSomeContainer("aa", "bb");
Mockito.when(someDatamodelService.getAttributes()).thenReturn(Arrays.asList("aa", "bb"));
JsonNode node = someSpringConverter.convert(someContainer, JsonNode.class);
Mockito.verify(someDatamodelService.getAttributes());
assertTrue(node.get("bb") != null);
}
}

Related

Clone Spring controller instance for testing purpose

Is it possible to clone an instance of a Spring #Controller in a JUnit 5 test?
I would like to do so for the following reasons :
Mocking pollutes the context for next tests and DirtiesContext has a huge impact on performance. So I don't want to use Mocks unless the modification sticks to the test class.
I would like to run tests in parallel so modifying a shared controller instance at runtime (with ReflectionUtils for example) will produce unpredictable behavior.
I don't want to set a controller as prototype as it is not the case at runtime and Spring is already wiring all dependencies.
I was thinking to inject the controller in the test class with #Autowired as usual and then making a deep copy of it with SerializationUtils like described in this post but I hope there could be a more robust approach than serializing / deserializing in every test class.
What makes tests failing in parallel mode?
The end-to-end tests are using common services. For example, I have a controller used in those two tests.
#RestController
#RequestMapping("/api/public/endpointA/")
public class SomeController {
#Autowired
private MyService myService;
#GetMapping("/{id}")
public Something getSomethingById(#PathVariable int id) {
return myService.getSomethingById(id);
}
}
The first test just check the standard usage.
class SomeControllerTest {
#Autowired
#InjectMocks
private SomeController someController;
#Test
void testGetSomethingById() {
Assertions.assertEquals(
1,
// I use some custom wrapper for the HTTP methods calls on
// the controller like this to ease the mock mvc setup.
someController.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
).getId()
);
}
}
The second case test what happens when error occurs.
class SomeControllerExceptionTest {
#Autowired
private SomeController someController;
#SpyBean
private MyService myService;
#BeforeEach
public void before() {
// Mock to test error case
doThrow(RuntimeException.class).when(myService)
.getSomethingById(anyInt());
}
#Test
void testGetSomethingById() {
Assertions.assertThrows(
someController.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
)
);
}
}
By mocking in the second test, I'm not sure the first test won't use the mocked instance of the second test when tests are running in parallel.
Instantiate the controller in the test method yourself, mock the dependencies inside the method and inject them in the controller.
Same situation described above but I mock on a new instance of the controller.
#RestController
#RequestMapping("/api/public/endpointA/")
public class SomeController {
#Autowired
private MyService myService;
#GetMapping("/{id}")
public Something getSomethingById(#PathVariable int id) {
return myService.getSomethingById(id);
}
}
Test both cases in one test.
class SomeControllerTest {
#Autowired
private SomeController someController;
private SomeController someControllerMocked;
#BeforeEach
public void before() {
someControllerMocked = new SomeController();
MyService mockedService = mock(MyService.class);
doThrow(RuntimeException.class).when(mockedService)
.getSomethingById(anyInt());
ReflectionTestUtils.setField(
someControllerMocked,
"myService",
mockedService
);
}
#Test
void testGetSomethingById() {
Assertions.assertEquals(
1,
someController.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
).getId()
);
}
#Test
void testGetSomethingByIdException() {
Assertions.assertThrows(
someControllerMocked.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
)
);
}
}
Yes! It's working and the context is not polluted. Ok let's say I have 10 services injected in my controller actually. I will have to do ReflectionUtils#setField 9 times for the legacy services and 1 time for the mock. Looks a bit ugly.
With AutowireCapableBeanFactory to the rescue. I've managed to clean this a little bit.
Same situation, but SomeController has 10 autowired services.
class SomeControllerTest {
#Autowired
private SomeController someController;
private SomeController someControllerMocked;
#Autowired
private AutowireCapableBeanFactory beanFactory;
#BeforeEach
public void before() {
someControllerMocked = new SomeController();
// Replace the 9 ReflectionUtils#setField calls with this
beanFactory.autowireBean(someControllerMocked);
MyService mockedService = mock(MyService.class);
doThrow(RuntimeException.class).when(mockedService)
.getSomethingById(anyInt());
ReflectionTestUtils.setField(
someControllerMocked,
"myService",
mockedService
);
}
#Test
void testGetSomethingById() {
Assertions.assertEquals(
1,
someController.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
).getId()
);
}
#Test
void testGetSomethingByIdException() {
Assertions.assertThrows(
someControllerMocked.getAndParse(
HttpStatus.OK,
Something.class,
"/{id}",
1
)
);
}
}
I think it's the best approach I've found.

Is there a way to test nested objects without the web or persistence layer in Spring Boot?

I'm using JUnit5 to test a Spring Boot application. I want to test a #Service object, which uses #Autowired fields. I would like to mock another #Service object which is indirectly used by my test object. Concretely, I have the following (highly simplified) setup:
Object being tested:
#Service
public class MainService {
private #Autowired SubService subService;
public String test() {
return subService.test();
}
}
SubService:
#Service
public class SubService {
private #Autowired StringService stringService;
public String test() {
return stringService.test();
}
}
StringService:
#Service
public class StringService {
public String test() {
return "Real service";
}
}
Test class used:
#SpringBootTest
public class MainServiceTest {
private #Autowired MainService mainService;
private #MockBean StringService stringService;
#BeforeEach
public void mock() {
Mockito.when(stringService.test()).thenReturn("Mocked service");
}
#Test
public void test() {
assertEquals("Mocked service", mainService.test());
}
}
The above works if I run the test class as a #SpringBootTest, but this loads the full application and is very slow. I also want to avoid #WebMvcTest since I don't need the web server, or #DataJpaTest since I don't need persistence. I don't want to mock SubService, as it contains functionality I want to test together with the MainService.
I tried the following:
#ExtendWith(SpringExtension.class) => throws NoSuchBeanDefinitionException, it seems the autowiring does not work in this case
#ExtendWith(MockitoExtension.class) and using #InjectMocks and #Mock instead of the Spring annotations => as the StringService is not a direct field of the MainService being tested, this does not work.
Is there a way to use the spring dependency injection system without loading the web server or persistence layer, or alternatively not use Spring tests but allow for 'nested' dependency injection?
You can use profiling (i.e Spring #Profile) to avoid loading the whole application. It will look something like below:
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
public MainService mainService() {
return new MainService();
}
#Bean
public SubService subService() {
return new SubService();
}
// mock the StringService
#Bean
public StringService stringService() {
return Mockito.mock(StringService.class);
}
}
then use that profile with `#SpringBootTest(classes = TestConfiguration.class), it will look something like below:
#ActiveProfiles("test")
#SpringBootTest(classes = TestConfiguration.class)
class MainServiceTest {
#Autowired
private MainService mainService;
#Test
public void test() {
// configure behavior using apis like when(), basically however you
// want your mock to behave
}
}
This will load only the beans defined in the class TestConfiguration.
NOTE: Since your question is more about how to avoid loading the whole application, I've answered focusing on that. The above approach will get the job done, but I'd prefer constructor wiring over any other mode of dependency injection on any given day, it's easier to maintain and test(like cases where you want to mock).

How to mock context.getBeansWithAnnotations with Mockito

I have created an interface Client with its two concrete implementations
clientA and clientB and annotated them with my custom annotation.
public interface Client{
public void dosomething();
}
#Component
#Myannotation
public class clientA implements Client {
public void doSomething(){
sysout("Client A do something");
}
}
#Component
#Myannotation
public class clientB implements Client {
public void doSomething(){
sysout("Client B do something");
}
}
Now I am calling the overriden methods of both clientA and clientB from Alien class.
#Component
class Alien{
#Autowired
private ApplicationContext context;
public void performOperation(){
Map<String, Object> beans =
context.getBeansWithAnnotation(MyAnnotation.class);
for(Map.Entry<String, Object> entry: beans.entrySet()) {
Client c = (Client)entry.getValue();
c.doSomething();
}
}
}
I am facing problem with writing test method for performOperation.
#RunWith(MockitoJUnitRunner.class)
class AlienTest
{
#InjectMocks
Alien a;
#Test
public void testperformOperation(){
//how to Mock for beans
assertEquals(expected, a.performOperation());
}
}
1) How should I write testperformOperation method(allowed to change the return type of performOperation method from void to any other type)
2) Is there any better way to get list of all implementations for Client interface without creating custom annotations.
I would suggest you first refactoring Alien to make it more testable using Dependency Injection idea which its dependencies (i.e Client) can be injected from outside rather than hard coded inside a method which always get from the spring context:
#Component
public class Alien{
private List<Client> clients = new ArrayList<>();
#Autowired
public Alien(List<Client> clients) {
this.clients = clients;
}
public void performOperation(){
for(Client c: clients) {
c.doSomething();
}
}
}
If you simply want to inject all Client implementation to the Alien , you just need to #Autowired List<Client> into Alien which Spring will already help you to inject all the Client implementation to it out of the box. No need to create #Myannotation
Once you make the Alien 's dependencies injectable (i.e a list of client) , you can simply inject a mock to it and verify performOperation() really invoke all of Client 's doSomething():
#RunWith(MockitoJUnitRunner.class)
class AlienTest{
#Mock
private Client mockClientA;
#Mock
private Client mockClientB;
#Test
public void testperformOperation(){
List<Client> clients = new ArrayList<>();
clients.add(mockClientA);
clients.add(mockClientB);
Alien alien = new Alien(clients);
alien.performOperation();
verify(mockClientA).doSomething();
verify(mockClientB).doSomething();
}
}
I’ll answer both parts of your question, but I believe the first approach is inferior and the second is the go-to approach.
If you want to stick with your custom annotation approach, you need to have a #Mock ApplicationContext applicationContext in your test class. In the test method (or setup method) you need to mock the call to applicationContext.getBeansWithAnnotation and return an appropriate map containing your bean (possibly also a mock)
You can easily inject all beans to a class by injecting a List of the appropriate type. In your case
get rid of #Autowired ApplicationContext
add an #Autowired List (or, preferably, use constructor injection)
This will also make the tests simpler, no need to mock ApplicationContext.
For example, see https://dzone.com/articles/load-all-implementors

MockBean stubbing ineffective

I have a configuration class with a few MockBeans replacing actual beans in context for tests.
#Configuration
public class MyTestConfig {
#MockBean
private MyService myService;
}
I use those mocks in my tests:
#Import({ MyTestConfig .class })
public class MyTest {
#Autowired
private MyService myService;
#Test
public void aTest() {
...
}
}
First the idea was to add the stubbing in this MyTestConfig configuration class, so that the mock is pre-made for all tests, so I did it in a #PostConstruct method, and it worked just fine - the mock in test did return the expected value:
#PostConstruct
public void init() {
when(myService.foo("say hello")).thenReturn("Hello world");
}
It turned out though, that constructing a pre-made mock suitable for all test can be tricky, so we decided to move the stubbing to tests.
#Test
public void aTest() {
when(myService.foo("say hello")).thenReturn("Hello world");
}
And this doesn't work - the stubbed method returns null. We want to leave MockBeans in the configuration class, but stub them in tests, so any advice on why the stubbing is ineffective?
Spring Boot 2.0.5, Mockito 2.22.0
Yes, stubbing should be performed inside their respective test cases (unless you have a test class that shares the stubbing scenarios but it all comes down to preference).
However, for creating #MockBeans, you would need to use a #SpringBootTest in order to get the actual beans replaced with mocks. This could be done as simply as this example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private MyTestClass testClass;
#MockBean
private MyService service;
#Test
public void myTest() {
// testing....
}
}

Unit Testing a class with ServiceLocatorFactoryBean Autowired

I have a Interface which is registered as part of ServiceLocatorFactoryBean. The main purpose of this Interface is to act as a factory.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
I have "autowired" this Interface in various classes, that I want to test now with Mockito.
The issue is Mockito doesn't support interfaces. How can inject a mock of this interface in the class I am testing.
The only alternative I see is to run the test using SpringJunitRunner and providing an Application Context which has the bean configurations. But this is too verbose.
I take it you'd like to spy on the implementation that Spring generated for your interface?! That's close to impossible to achieve with what you have so far... However there are at least the following alternatives below.
Suppose we have the following setup:
public interface MyService {
String doIt();
}
#Component
public static class ServiceConsumer {
#Autowired
private MyService service;
public String execute() {
return service.doIt();
}
}
0) Later edit: while roaming around, I found that it may be possible to spy and even replace an autowired field with a mock, and fairly easy too, using Springockito-annotations.
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class, classes = {SpringockitoConsumerTest.class})
public class SpringockitoConsumerTest {
#WrapWithSpy(beanName = "myService")
#Autowired
private MyService mockService;
#Autowired
private ServiceConsumer consumer;
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(mockService).doIt();
}
}
If Springockito-annotations is out of the question, please see the 2 original suggestions below
1) You could just create your mock of the interface and auto-inject it Mockito in your bean. This is the simplest solution (I could think of at the time of writing) but it does not ensure that the #Autowired annotation was not forgotten in the consumer (perhaps a dedicated test could be added?!):
public class AutoInjectMocksConsumerTest {
#Mock
private MyService serviceMock;
#InjectMocks
private ServiceConsumer consumer = new ServiceConsumer();
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
when(serviceMock.doIt()).thenReturn("allDone");
}
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(serviceMock).doIt();
}
}
2) Alternatively as you also said, you could run it with the SpringJunitRunner making a minimum of effort to define and instantiate the necessary Spring context while also providing your own service mock. Albeit people may complain this solution is not that clean, I find it sufficiently elegant and it also validates that the #Autowired annotation was not forgotten in the consumer implementation.
#RunWith(SpringJUnit4ClassRunner.class)
#Configuration
#ComponentScan
#ContextConfiguration(classes = {SpringAutowiringConsumerTest.class})
public class SpringAutowiringConsumerTest {
#Autowired
private MyService mockService;
#Autowired
private ServiceConsumer consumer;
#Test
public void shouldConsumeService() {
assertEquals("allDone", consumer.execute());
verify(mockService).doIt();
}
#Bean
public MyService mockService() {
MyService serviceMock = mock(MyService.class);
when(serviceMock.doIt()).thenReturn("allDone");
return serviceMock;
}
}

Resources