How to mock a spring bean? - java

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.

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.

Are there any use cases when it is acceptable to use Spring beans created by constructor call?

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.

Inject JMockit mocks into Spring context

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

Unit Test using the DomainClassConverter feature of Spring

I'm trying to code a unit test for a method defined in a controller.
The method is like this:
#RestController
#RequestMapping("/products")
public class RestProductController {
#RequestMapping(value="/{product}/skus", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public List<SkuSummaryVO> getSkuByProduct(#Valid #PathVariable Product product){
List<SkuSummaryVO> skusByProductVOs = skuService.getSkusByProduct(product);
return skusByProductVOs;
}
}
We use in our Configuration class the annotation #EnableSpringDataWebSupport to enable the DomainClassConverter feature. So we can use the JPA entity as a #PathVariable. So when a product id will be set in the URL, we will get the product (with a request behind the scene).
We are developing unit test without enabling the Spring App Context and using Mockito.
So we initialize the mockMvcBuilders like this:
public class RestProductControllerTest {
...
#Before
public void setUp() {
RestProductController restProductController = new RestProductController();
...
mockMvc = MockMvcBuilders.standaloneSetup(restProductController).build();
}
}
and the test method is like this:
#Test
public void testGetProductById() throws Exception {
...
String jsonResult = ...;
mockMvc.perform(get("/products/123/skus").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string(jsonResult));
}
And I get a 500 for the HttpCode (the status)
And the unit tests work fine for the controller methods witch are not using the DomainClassConverter feature (for example if I use a Long productId instead of a Product product as a parameter of getSkuByProduct, it will work)
UPDATE: on second thought, what I originally proposed below could never work since DomainClassConverter requires that your Spring Data Repositories be present in the ApplicationContext, and in your example you are using StandaloneMockMvcBuilder which will never create an ApplicationContext that contains your Spring Data Repositories.
The way I see it, you have two options.
Convert your test to an integration test using a real ApplicationContext (loaded via #ContextConfiguration as demonstrated in the reference manual) and pass that to MockMvcBuilders.webAppContextSetup(WebApplicationContext). If the configured ApplicationContext includes your Spring Data web configuration, you should be good to go.
Forgo the use of DomainClassConverter in your unit test, and instead set a custom HandlerMethodArgumentResolver (e.g., a stub or a mock) via StandaloneMockMvcBuilder.setCustomArgumentResolvers(HandlerMethodArgumentResolver...). Your custom resolver could then return whatever Product instance you desire.
You'll have to register an instance of the DomainClassConverter with the ConversionService in the StandaloneMockMvcBuilder that is created when you invoke MockMvcBuilders.standaloneSetup(Object...).
In SpringDataWebConfiguration.registerDomainClassConverterFor(), you can see that the DomainClassConverter is instantiated (and indirectly registered) like this:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<FormattingConversionService>(conversionService);
converter.setApplicationContext(context);
And you can set your own FormattingConversionService via StandaloneMockMvcBuilder.setConversionService(). See WebMvcConfigurationSupport.mvcConversionService() for an example of how to configure the ConversionService for a web environment.
The challenge then is how to obtain a reference to the ApplicationContext. Internally, StandaloneMockMvcBuilder uses a StubWebApplicationContext, but as far as I can see (prior to Spring 4.1) there is no way to access it directly without subclassing StandaloneMockMvcBuilder.
As of Spring Framework 4.1, you could implement a custom MockMvcConfigurer (which gives you access to the WebApplicationContext via its beforeMockMvcCreated() method.
So hopefully that's enough information to get you on the right track!
Good luck...
Sam
You can mock the string to entity conversion through WebConversionService. Check this answer for some code example and more details.

Categories