Inject JMockit mocks into Spring context - java

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;
}
}

Related

Spring MVC 4 for REST Controller, how to autowire different dependencies for test cases

Let's say you have a controller like so and you want to change the autowired dependency. What are ways to have one implementation for your project under /src/main/java and then have a mock dependency in your test code under /src/test/java. The code under /src/main/java will get built into the web application.
#RestController
#RequestMapping("/u/util/")
public class HealthCheckAdapter {
#Autowired
private HealthDbService healthCheck;
}
The HealthDbService is a Java interface, and here an example concrete class. I could change the qualifier below, but Spring will still pick this up in the test cases?
#Repository
#Qualifier("basicHealthCheckService")
public class HealthCheckDefaultManager implements HealthDbService {
}
Simplest, recommended way: use constructor injection:
#RestController
#RequestMapping("/u/util/")
public class HealthCheckAdapter {
private HealthDbService healthCheck;
#Autowired // not even needed in recent versions
public HealthCheckAdapter(HealthDbService healthCheck) {
this.healthCheck = healthCheck;
}
}
And now in your unit test:
new HealthCheckAdapter(new FakeHealthDbService());
or, using a mocking API like Mockito:
HealthDbService mockHealthDbService = mock(HealthDbService.class);
new HealthCheckAdapter(mockHealthDbService);
If you use Mockito, it can also inject mock dependencies using annotations, even if you keep using field injection.
And finally, if you want to use mock MVC tests (to test the annotations, the JSON serialization, etc.), you can use the Spring testing support (which is even easier to use in Spring Boot)

Autowired implementation based on profile Spring Boot

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.

How to mock a spring bean?

I'm trying to mock an class that uses JAXRS and this class are a spring component.
#Component
public class PostmanClient {
private WebTarget target;
public PostmanClient() {
Client client = ClientBuilder.newClient();
target = client.target(...);
}
public String send(String xml) {
Builder requestBuilder = target.request(MediaType.APPLICATION_XML_TYPE);
Response response = requestBuilder.post(Entity.entity(xml, MediaType.APPLICATION_XML_TYPE));
return response.readEntity(String.class);
}
}
This is my test method:
#Test
public void processPendingRegistersWithAutomaticSyncJob() throws Exception {
PostmanClient postmanClient = mock(PostmanClient.class);
String response = "OK";
whenNew(PostmanClient.class).withNoArguments().thenReturn(postmanClient);
when(postmanClient.send("blablabla")).thenReturn(response);
loadApplicationContext(); // applicationContext = new ClassPathXmlApplicationContext("/test-context.xml");
}
When i debug the postmanClient instance, its a instance created by Spring and not a mock.
How can i avoid this behavior and get a mock instance ?
If you are using PowerMock with Spring, you should consider following tips:
1. Use #RunWith(SpringJunit4Runner.class)
2. Use #ContextConfiguration("/test-context.xml") // load spring context before test
3. Use #PrepareForTest(....class) // to mock static method
4. Use PowerMockRule
5. The simplest way to mock a spring bean is to use springockito
Back To You Question:
If I don't understand you wrong, you have PostmanClient defined in spring context, which means, you only need use springockito to achieve your goal, just follow the tutorial on springockito page.
You can use BDD framework Spock to write UT for your Spring Framework. With Spock Spring extension (Maven: groupId:org.spockframework, artifactId:spock-spring), you can load Spring context in your unit test.
#WebAppConfiguration
#ContextConfiguration(classes = Application.class)
class MyServiceSpec extends Specification {
#Autowired
UserRepository userRepository
}
If you have some beans you want to mock them instead of loading from Spring context, you can add following annotation on the bean you want to mock.
#ReplaceWithMock
This article has the detailed intro about how to write UT for your Spring app with Spock.
Im not sure what is wrong with your implementation. Maybe PostmanClient should be an interface instead of class?
However I've implemented a similar unit test in my practice/test project. Maybe it will help:
https://github.com/oipat/poht/blob/683b401145d4a4c2bace49f356a5aa401fe81eb1/backend/src/test/java/org/tapiok/blogi/service/impl/PostServiceImplTest.java
Register your mock with Spring-ReInject before the application context starts in your test's constructor. The original bean will be replaced with a mock, and the mock will be used by Spring everywhere, including injected fields. The original bean will be never created.
There is an option to fake Spring bean with just plain Spring features. You need to use #Primary, #Profile and #ActiveProfiles annotations for it.
I wrote a blog post on the topic.
I've created the example to answer another question, but also cover your case. Simple replace MockitoJUnitRunner via SpringJUnit4ClassRunner.
In nutshell, I create Java Spring Configuration which includes only classes which should be tested/mocked and returns mocked object instead really object, and then give Spring does it work. Very simple and flexible solution.
It also works fine with MvcMock
As of Spring Boot 1.4.x you can use new annotation called #MockBean.

Spring / Mock components that are retrieved by using the context directly

We have a project that uses Spring annotations to configure its context.
To test this project we are using Mockito and it's Spring extension.
In tests I need to override some beans with mock/spy version.
With the #Mock/#Spy and #InjectMock annotations I have been able to use spy for beans using autowiring mechanism.
Now I have a third party component which create another Spring context and then merge the 2 contexts together. This third party component retrieve beans using a call to the context:
applicationContext.getBean(key);
In this case, the #Mock/#Spy and #InjectMock combination is not working.
The 'workaround' solution I have put in place is an XML file in which I declare my spied bean:
<mockito:spy beanName="beanToSpy"/>
To stay in the annotation world, I have tried springockito-annotations as mentioned in these similar questions:
Injecting Mockito mocks into a Spring bean
and its duplicate:
How to inject a Mock in a Spring Context
But the bean is not spied or mocked, I've probably a configuration error.
My current setup is:
A base class that is in charge of the Spring config for test:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#ContextConfiguration(loader= SpringockitoContextLoader.class, locations ={"/config.xml","/test-config.xml"})
public abstract class BaseTest {
//...
}
A test class that would like to use a mocked bean:
public class MyTest extends BaseTest {
#ReplaceWithMock #Autowired MyService myService;
#WrapWithSpy #Autowired OtherService otherService;
#Test public void someTest() {
doReturn(x).when(myService).call();
doReturn(y).when(otherService).process();
}
}
Unfortunately in MyTest, the beans are not replaced by their mock/spy versions, it is the plain old regular version.
EDIT:
The third party component that does the lookup is using its own spring parent context and add the current spring context into its own. The lookup to retrieve the component that I want to be mocked occurs after the context has been fully loaded.
What should I do to properly replace the bean in the context with a mock/spy version ?
What is wrong with the way I'm using #WrapWithSpy / #ReplaceWithMock ?
When does the call to
applicationContext.getBean(key);
happens? Is it possible that it retrieves the bean before the SpringockitoContextLoader has a chance to replace it with a spy?
One solution to stay in annotation world would be to override the bean in java config:
#Bean
public MyBeanType myBeanType() {
return spy(new MyBeanType(...))
}
The more conventional way to perform this is by simply mocking them in the test as required :
public class MyTest extends BaseTest {
#Test public void someTest() {
MyService myMockService = Mockito.mock(MyService.class);
// define when's
// perform verification
}
You can inject using SpringReflectionTestUtils, or if using setter injection just set normally.
If you use a global mocked bean in a test class with multiple tests, the results can get confusing.

How to inject a Mock in a Spring Context [duplicate]

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

Categories