This question already has answers here:
Injecting Mockito mocks into a Spring bean
(23 answers)
Closed 9 years ago.
I have a test that is using some Spring contexts. In these contexts, a number of beans are declared.
I want the test to use the actual implementation of the beans of the contexts, EXCEPT for one of them, for which I want to use a MOCK.
I tried to make the Test a Configuration component (with #Configuration annotation), but the XML seems to take priority over the #Bean annotation, so it doesn't work, this way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"context1.xml", "context2.xml", ...})
#Configuration
public class MyTest{
#Inject
private MyTargetBean target;
private AnotherBean myMock = mock(AnotherBean.class);
#Bean
public AnotherBean myMock() { return myMock; }
.....
I know that i can define the Mocks in XML, but for that I would need an extra XML file for each test in which I wish to do this. I want to avoid this complexity.
Is there a way to inject a bean (like a mock) in a context apart than through XML?
Thank you!
Yes, you are on the right track, putting a mock #Bean in a #Configuration class is one approach, and I'll describe my experience:
The trick is that you need to use a different set of .xml files purely for testing which exclude the live versions of those beans.
#ContextConfiguration(locations = {"context1-test.xml", "context2-test.xml", ...})
And the "-test-xml" files go in src/test/resources.
At least that was my experience in doing the same thing. Maybe there is some way to "override" the beans with the mock versions, but as yet I am not aware of it.
I also chose to put the mocks (I had 5 of them) all together in an own configuration:
#Configuration
public class MockServicesProvider {
#Bean
public AnotherBean myMock() { return mock(AnotherBean.class); }
}
Another interesting part of this problem is the common usage of initMocks(this); in the #Before method of your test class.
If the mocks are being used in other places (and they are, that's why you are wiring them up...) then initMocks(this) will blow them away between tests (not literally - just that new mocks will be created and any other mocks wired up in other objects will be "lost").
The solution to this was to call mockito's reset(mockObject) in the #Before method before each test. The same mocks are reset (all the when's and interactions), without creating new mocks.
Note that the Mockito docs for reset say very sternly that this method should not commonly be used, except in the context of mocks being applied via dependency injection, as we are indeed doing in this case :)
Have fun!
It is indeed a duplicate of
Injecting Mockito mocks into a Spring bean
The Springockito-annotation is exactly what I was looking for
https://bitbucket.org/kubek2k/springockito/wiki/springockito-annotations
Related
I'm developing a MessageHandler class which contains a Cache field which is also I developed, MessageHandler is annotated as a #Component and Cache is not bean.
When using Spring-boot-test to test the MessageHandler class, I use #Mockbean to mock a MessageHandler and try to use setter to set a Cache object for the mock bean. But when I run test to see if MessageHandler has a cache, I got null.
My code is as follows:
#MockBean(value = HiMessageHandler.class)
MessageHandler messageHandler;
public void setUp(){
Cache cache=new Cache();
cache.offer(new HiMessage("1","1","1"));
messageHandler.setCache(cache);
}
#Test
public void testConsume() throws Exception {
setUp();
System.out.println(messageHandler.getCache());
}
I want to know how to set a field that is not a bean for a mock bean.
I think the main issue here is that you try to mock the class that you test.
You test MessageHandler class in an integration test (otherwise, if its a unit test - then spring boot test is not required)
So the goal is to check that the code that you wrote in MessageHandler really works, and since its an integration test, probable with an emphasis on interaction with other real/semi-real components.
Side note, if you're not sure about the difference between unit and integration tests feel free to read my answer on this question or just google, it will provide more valuable resources for sure, the point is you should clearly understand the difference between the two and pick the right tool for the job.
On the other hand, when you use #MockBean annotation, Spring creates a Mock for your bean, and puts this mock instead of the real bean into application context, so that other beans, if dependent on this message handler, could get the reference on this mock instead of the reference on the real bean.
Then you call some method on a mock (setCache) and indeed it doesn't work, because mock is not a real thing, its a mock, the correct way to interact with mock is to specify interaction rules (style when(...).thenReturn(...) in mockito syntax). Yes, it doesn't work, so this is a direct answer on your question, but as I've explained above the issue is deeper here IMHO.
I am writing unit tests for my services in Spring, Java. I mock all dependencies in a tested class and I instantiate the tested class in a constructor, to which I pass mocked classes. The problem is that the tested class injects properties from .properties file into fields that are inside it (let's say Strings).
I use the standard combination of #PropertySource on a class level and #Value on a field level inside my tested class.
As we know, properties injection fails when class is instantiated through constructor (not as a bean during the Spring Container initialization). How do you deal with such problem?
I've got one solution, though I think it is bad and unsatisfactory, that is:
1. to #Autowire the class under test normally, then replace all its dependencies by using a setter.
I also know about the #TestPropertySource annotation and if I understand correctly, it does not provide a solution and it is only a way to override already existent properties - which is not the case, as we cannot really use any properties.
Thanks for help in advance :)
It is rather straight : in your unit test, inject the property in a String field and create the object under test not in the constructor of the test class but in the hook method invoked after the container has loaded the Spring context.
In JUnit 4, you specify this hook method with #Before and in JUnit 5 with #BeforeEach.
It would give something like :
#RunWith(SpringJUnit4ClassRunner.class)
public class FooTest{
Foo foo;
#Value("${myProp}")
String myProp;
#BeforeEach
public void beforeEach(){
foo = new Foo(myProp);
}
}
Note that to make your test be executed faster you should load from the Spring context only what your test requires : the environment part.
I am developing a REST API with Spring Boot.The problem it's that I have one interface and two implementations and I want to test only with the mock implementation.
Interface CRMService
#Service
CRMServiceImpl
#Service
CRMServiceMock
Implementations: the first one is the real integration with the backend and the second is a mock for testing purposes, what's the best approach? Integration test or test based on the active profile ? If I need to autowire a service based on profile what's the best practice?
While I'm sure there's exceptions, generally it shouldn't be integration or unit tests (often involves mocks), but both; see testing pyramid concept.
Integration tests: just use the real service. If it calls out to other live services, then consider injecting the URLs as Spring Boot properties which point to mock servers in the test environment (Node.js or something easy and quick).
Unit tests: Consider using a test-framework like Mockito. Using this you can write your tests with mocks approximately like so:
private CRMServiceImpl mockService = mock(CRMServiceImpl.class);
#Test
public void someTest() {
when(mockService.someMethod(any(String.class), eq(5))).thenReturn("Hello from mock object.")
}
The above example roughly translates to "when some class invokes 'someMethod(String, int)' on your service, return the String specified".
This way allows you to still use mocks where necessary, but avoids having to maintain entire mock implementation profiles and avoids the problem of what to auto-wire.
Finally, if you need a full separate implementation, consider not auto-wiring services! Instead, use #Bean annotations in your configuration class and inject it via constructors into the classes that need it. Something like so:
#Configuration
public class ApplicationConfiguration {
#Value{$"service.crm.inmem"} // Injected property
private boolean inMem;
#Bean
CRMService getCRMService() {
if (inMem) {
return new CRMServiceMock();
}
return new CRMServiceImpl();
}
#Bean
OtherService getOtherService() {
// Inject CRMService interface into constructor instead of auto-wiring in OtherService.class
return new OtherService(getCRMService());
}
}
An example of when you could use ^^ would be if you wanted to switch between an in-memory store, and a real database-connection layer.
Personally I'd suggest doing dependency injection like the above example even when there aren't multiple implementations since as a project grows, if an auto-wired property fails it can be difficult to track down exactly why. Additionally explicitly showing where dependencies come from can help with organizing your application and visualizing your application hierarchy.
I am pretty new to Spring and I study using "Spring in Action" (fourth edition) by Craig Walls. The interest is not only on how to write code that is working, but also on the correct principles of using Spring.
Regarding the following piece of code from page 142, Listing 5.6:
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
My questions are generated by the following thoughts:
The general understanding is that Spring uses Dependency Injection as a way to reduce the management overhead of object dependencies, increase modularity, simplify testing and code reuse. However, doesn't it imply that beans must be created and managed by the container? Since I started reading on the subject, the first detail that I memorized stated that new should never appear in a well-written piece of code that follows DI.
Could this be a solution in case we want to test a Stateful bean? I mean, if there are multiple independent tests to be run on the same instance, each of them testing the same state of the bean. Even though I found out that there is a suitable annotation for doing this (#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)).
Is there another use case that is difficult or impossible to solve otherwise (except for using new)?
A more 'to the letter' implementation would use #ContextConfiguration to specify the ApplicationContext.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class HomeControllerTest {
#Autowired
HomeController controller;
#Test
public void testHomePage() throws Exception {
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
yes, you shouldn't use new to create Spring bean instances (otherwise they're not Spring beans anymore) in production code. But the whole point of DI is to let you create and manually inject your objects with fake dependencies in unit tests. So the test code here is perfectly fine.
Yes, each unit test is free to create its own bean instance and to populate it the way it needs to. Stateful beans are extremely rare, though. They're usually stateless.
Another place where using new to create a Spring bean is precisely in #Bean-annotated methods of configuration classes. The whole point of these methods is precisely to create and initialize the Spring beans that will then be used and injected by Spring. But again, using new in unit tests is absolutely fine, and the right thing to do.
For some integration tests, we use Spring’s #ContextConfiguration to create a real Spring context during the test. Now, it’s not supposed to be a full integration test, so we need a whole bunch of the Spring beans as mocks. This is not too complicated using Mockito and Spring’s factory-method, and even easier with Springockito.
But, this is using Mockito, while we are just migrating to JMockit. I would much prefer to use JMockit here as well. Basically, I am looking for a replacement for Springockito that uses JMockit instead.
I can also do it by hand. However, Mockito and JMockit seem to differ in one very important way: While in Mockito, you create mocks imperatively using a call to a method, in JMockit you get mocks declaratively ‘injected’ into your test. That’s too late to populate the Spring context. So if anyone can answer that, I’m happy as well: How can you create a mock in JMockit in your code?
If you are using Spring Test to do all the injection, then you can just let it do the job of creating instances for all dependencies, while having them mocked through suitable mock fields/parameters declared with the #Mocked or #Capturing annotations. The latter one will mock any implementation class that Spring has chosen to instantiate, even though the type used in the mock declaration is an interface or base class.
Alternatively, you could just let JMockit itself resolve all dependencies, by using #Tested(fullyInitialized = true) for top-level tested objects, with mocked dependencies provided as #Injectable's.
A "dirty" trick we use with Spring and Integration Tests while we still need to mock something, is to replace - where required - the real configuration, e.g.
#Configuration
#Profile("!test")
public class MyConfig {
#Bean
public MyBean bean() {
/** Real bean **/
}
}
with a mock one
#Configuration
#Profile("test")
public class MyTestConfig {
#Bean
public MyBean bean() {
final MyBean bean = mock(MyBean.class);
when(bean.doSomething()).thenReturn(withReply());
return bean;
}
}
it works with a "real" Spring Integration Test context and Mockito, it should work with JMockit as well, as long as you are able to create your bean with JMockit version of your class: basically something equivalent to mock(MyBean.class).
Edit: Whilst I am not familiar with JMockit, it seems that an equivalent way could be
#Configuration
#Profile("test")
public class MyTestConfig {
#Injectable MyBean mockXyz;
#Bean
public MyBean bean() {
/** You can probably mock its behaviour **/
return mockXyz;
}
}