Injecting mock #Service for Spring unit tests - java

I am testing a class that uses use #Autowired to inject a service:
public class RuleIdValidator implements ConstraintValidator<ValidRuleId, String> {
#Autowired
private RuleStore ruleStore;
// Some other methods
}
But how can I mock ruleStore during testing? I can't figure out how to inject my mock RuleStore into Spring and into the Auto-wiring system.
Thanks

It is quite easy with Mockito:
#RunWith(MockitoJUnitRunner.class)
public class RuleIdValidatorTest {
#Mock
private RuleStore ruleStoreMock;
#InjectMocks
private RuleIdValidator ruleIdValidator;
#Test
public void someTest() {
when(ruleStoreMock.doSomething("arg")).thenReturn("result");
String actual = ruleIdValidator.doSomeThatDelegatesToRuleStore();
assertEquals("result", actual);
}
}
Read more about #InjectMocks in the Mockito javadoc or in a blog post that I wrote about the topic some time ago.
Available as of Mockito 1.8.3, enhanced in 1.9.0.

You can use something like Mockito to mock the rulestore returned during testing. This Stackoverflow post has a good example of doing this:
spring 3 autowiring and junit testing

You can do following:
package com.mycompany;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
#Component
#DependsOn("ruleStore")
public class RuleIdValidator implements ConstraintValidator<ValidRuleId, String> {
#Autowired
private RuleStore ruleStore;
// Some other methods
}
And your Spring Context should looks like:
<context:component-scan base-package="com.mycompany" />
<bean id="ruleStore" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg index="0" value="com.mycompany.RuleStore"/>
</bean>

Related

Can't inject Bean (child of #Spy injected bean) in my Test

When I'm making a Test, I can't get injected a property of one of the injected beans (with #Spy). I am using Mockito to test.
I tried using #Mock, #Spy, #SpyBean and #InjectMocks in this Bean on my test but I can't get it injected.
#RunWith(MockitoJUnitRunner.class)
public class MyTest{
#InjectMocks private MyService = new myService();
#Spy private MyFirtsDepen firstDepen;
#Autowired #Spy private ChildDepen childDepen;
... More mocks and tests
}
#Service
public class MyService {
#Autowired private MyFirstDepen firstDepen;
....
}
#Mapper
public class MyFirstDepen {
#Autowired private ChildDepen childDepen;
....
}
#Component
public class ChildDepen {
...
}
When my test use firstDepen is working great, but when firstDepen uses childDepend always get Nullpointer. How can I inject this property in my test?
Since your MyFirtsDepen is a mock, there is no way to inject anything to it. Configure mock to return another mock.
when(firstDepen.getChildDepen()).doReturn(childDepen);

Spring Rest Docs with JUnit 5 and Webflux

I am unable to get a Spring Rest Docs test with JUnit 5 and Webflux working.
I have a working integration test with #WebFluxTest like this:
#WebFluxTest(SomeController.class)
class SomeControllerTest {
#Autowired
private WebTestClient testClient;
#MockBean
private SomeService service;
#Test
void testGetAllEndpoint() {
when(service.getAll())
.thenReturn(List.of(new Machine(1,"Machine 1", "192.168.1.5", 9060)));
testClient.get().uri("/api/machines")
.exchange()
.expectStatus().isOk()
.expectBodyList(Machine.class)
.hasSize(1);
}
}
I now want to write a documentation test. According to the docs, something like this should work:
#SpringBootTest
#ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
class SomeControllerDocumentation {
private WebTestClient testClient;
#MockBean
private SomeService service;
#BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.testClient = WebTestClient.bindToApplicationContext(webApplicationContext)
.configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
#Test
void testGetAllEndpoint() {
when(service.getMachines())
.thenReturn(List.of(new Machine(1, "Machine 1", "192.168.1.5", 9060)));
testClient.get().uri("/api/machines")
.accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk()
.expectBody().consumeWith(document("machines-list"));
}
}
I however get:
org.junit.jupiter.api.extension.ParameterResolutionException:
Failed to resolve parameter [org.springframework.web.context.WebApplicationContext webApplicationContext]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.web.context.WebApplicationContext' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I am also wondering if #SpringBootTest is needed as annotation or if #WebFluxTest(SomeController.class) is also supposed to work.
I am using Spring Boot 2.1.3 with spring-restdocs-webtestclient as dependency.
Instead of injecting WebApplicationContext use this:
#Autowired
private ApplicationContext context;
#BeforeEach
void setUp(RestDocumentationContextProvider restDocumentation) {
client = WebTestClient.bindToApplicationContext(context)
.configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
As an alternative to the answer of Gilad Peleg, you can just change the type in the method argument from WebApplicationContext to ApplicationContext.
Note also that instead of #SpringBootTest, you can use #WebFluxTest(SomeController.class)
According to docs:
#AutoConfigureRestDocs can also be used with WebTestClient. You can
inject it by using #Autowired and use it in your tests as you normally
would when using #WebFluxTest and Spring REST Docs, as shown in the
following example:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
#RunWith(SpringRunner.class)
#WebFluxTest
#AutoConfigureRestDocs
public class UsersDocumentationTests {
#Autowired
private WebTestClient webTestClient;
}

Not able to use #Autowired with #Mock while creating unit test cases

I have a service say MainService and it has few managers which are initialised with #Autowired, and it is using some external service which are also #Autowired.
My purpose is to create unit test cases so that I can access the inmemory DB with managers, and want to mock the external service.
Now problem which I am facing is if I use #Autowired in my unit test and use #Mock for external services, then it doesn't use mock methods, Instead it uses the actual implementation. If I do #InjectMocks then it doesn't pick the data from repo as it doesn't find the respective dependencies for managers, and if I use #Autowired and #InjectMocks together it still not being able to use the Mocks.
Something like this
#Service
public class MainService extends AbstractService
{
#Autowired
Manager1 manager1;
#Autowired
Manager2 manager2;
#Autowired
Manager3 manager3;
#Trace(dispatcher = true)
public void mainMethod(int data)
{
int data1 = manager1.getData(int xyz);\\ getting data from DAO
int data2 = manager1.getData(int xyz);\\ getting data from DAO
int data3 = manager1.getData(int xyz);\\ getting data from
\\External Service
}
}
Now the test case I am writing is
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class TestClass {
#InjectMocks
#Autowired
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
Manager3 manager3;
#Before
public void initTest()
{
MockitoAnnotations.initMocks(this);
int dataFirst=1;
int dataSecond =2;
int dataThird=3;
int dataForMethod=4;
repoManager1.save(dataFirst);
repoManager2.save(dataSecond);
}
#Test
public void testMethod()
{
Mockito.when(manager3.getData(Mockito.anyInt())).thenReturn(dataThird);
service.mainMethod(dataForMethod);
}
}
This is a replication of the actual service, when I debug the test I found that the mock is not being used, its using actual implementation, and when I removed #Autowired from MainService then it only execute the mocked method.
Instead of using Field Injection(using #Autowired on class variables) use Constructor Injection. This way you can initialize your MainService class with some mocks and some real Implementations. Something like this
#Service
public class MainService extends AbstractService
{
private final Manager1 manager1;
private final Manager2 manager2;
private final SomeExternalService externalService;
#Autowired
public MainService(Manager1 manager1, Manager2 manager2, SomeExternalService externalService)
this.manager1= manager1;
this.manager2= manager2;
this.externalService = externalService;
}
........................
}
From your test class don't Autowire MainService. Just Autowire Manager1 and Manager2 and Create Mock for SomeExternalService(and initialize it). And then create instance of MainService using constructor.
public class TestClass {
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
SomeExternalService externalService;
#Before
public void setUp(){
service = new MainService(repoManager1, repoManager2, externalService);
}
}
If you want to use Mockito, you need Annotate your TestClass with #RunWith(MockitoJUnitRunner.class) instead of #RunWith(SpringRunner.class).
Then for the #AutoWired in the TestClass for repoManager1, repoManger2. Annotate them with #Mock instead of #Autowired, as you want to mock them with Mockito.
I have not used the SpringRunner myself, but from a quick read, I can see that it is used mostly for Integration Test, where you want to load the SpringContext..etc.
Also #ActiveProfiles("test") is mostly used for Integration Test, where you want to load the spring context, with the 'test' profile properties.

Spring's #Retryable not working when running JUnit Test

I have this test:
#RunWith(MockitoJUnitRunner.class)
public class myServiceTest {
#InjectMocks
myService subject;
private myService spy;
#Before
public void before() {
spy = spy(subject);
}
#Test
public void testing() {
when(spy.print2()).thenThrow(new RuntimeException()).thenThrow(new RuntimeException()).thenReturn("completed");
spy.print1();
verify(spy, times(3)).print2();
}
and then I have:
#Service("myService")
public class myService extends myAbstractServiceClass {
public String print1() {
String temp = "";
temp = print2();
return temp;
}
#Retryable
public String print2() {
return "completed";
}
}
then I have this interface(which my abstractService implements):
public interface myServiceInterface {
#Retryable(maxAttempts = 3)
String print1() throws RuntimeException;
#Retryable(maxAttempts = 3)
String print2() throws RuntimeException;
}
but, I get a runtimeexception thrown when I run the test, leading me to believe it is not retrying. Am I doing this wrong?
This is because you are not using the SpringJUnitClassRunner.
Mockito and your own classes are not taking the #Retryable annotation in account. So you rely on the implementation of Spring to do so. But your test does not activate Spring.
This is from the SpringJUnit4ClassRunner JavaDoc:
SpringJUnit4ClassRunner is a custom extension of JUnit's BlockJUnit4ClassRunner which provides functionality of the Spring TestContext Framework to standard JUnit tests by means of the TestContextManager and associated support classes and annotations.
To use this class, simply annotate a JUnit 4 based test class with #RunWith(SpringJUnit4ClassRunner.class) or #RunWith(SpringRunner.class).
You should restructure your test class at least to something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyConfig.class)
public class MyServiceTest {
#Configuration
#EnableRetry
#Import(myService.class)
public static class MyConfig {}
...
What am I doing there?
activate the Spring JUnit hook
specify the Spring context configuration class
define the spring configuration and import your service as a bean
enable the retryable annotation
Are there some other pitfalls?
Yes, you are using Mockito to simulate an exception. If you want to test this behaviour with Spring like this, you should have a look at Springockito Annotations.
But be aware of that: Springockito you will replace the spring bean completely which forces you to proxy the call of your retryable. You need a structure like: test -> retryableService -> exceptionThrowingBean. Then you can use Springockito or what ever you like e.g. ReflectionTestUtils to configure the exceptionThrowingBean with the behaviour you like.
You should reference the interface type of your service in your test: MyServiceInterface
And last but not least. There is a naming convention nearly all Java developers follow: class names have first letter of each internal word capitalized
Hope that helps.
Another way:
#EnableRetry
#RunWith(SpringRunner.class)
#SpringBootTest(classes={ServiceToTest.class})
public class RetryableTest {
#Autowired
private ServiceToTest serviceToTest;
#MockBean
private ComponentInsideTestClass componentInsideTestClass;
#Test
public void retryableTest(){
serviceToTest.method();
}
}
I think you should let Spring manage the bean, create the appropriate proxy and handle the process.
If you want to mock specific beans, you can create mocks and inject them to the service under test.
1st option could be unwrapping proxied service, creating mocks and manually injecting them:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class})
#DirtiesContext
public class TheServiceImplTest {
#Autowired
private TheService theService;
#Before
public void setUp(){
TheService serviceWithoutProxy = AopTestUtils.getUltimateTargetObject(theService);
RetryProperties mockRetryProperties = Mockito.mock(RetryProperties.class);
ReflectionTestUtils.setField(serviceWithoutProxy, "retryProperties", mockRetryProperties);
}
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
}
In this example, I mocked one bean, RetryProperties, and injected into the service. Also note that, in this approach you are modifying the test application context which is cached by Spring. This means that if you don't use #DirtiesContext, service will continue its way with mocked bean in other tests. You can read more here
Second option would be creating a test specific #Configuration and mock the depended bean there. Spring will pick up this new mocked bean instead of the original one:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class, TheServiceImplSecondTest.TestConfiguration.class})
public class TheServiceImplSecondTest {
#Autowired
private TheService theService;
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
#Configuration
static class TestConfiguration {
#Bean
public RetryProperties retryProperties() {
return Mockito.mock(RetryProperties.class);
}
}
}
In this example, we have defined a test specific configuration and added it to the #ContextConfiguration.

Context initialization issue for controller Unit testing with Spring 3.2 and Mockito

I'm trying to provide a clean Unit Test for a Controller of mine. This Controller has a Service as dependency and this Serviceh has a Datasource as dependency.
The test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ContentActionWebServiceControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private MyService myService;
#Test
public void getRequestActionList() throws Exception {
when(...)
perform(...);
verify(...);
}
#Configuration
#ImportResource("...")
static class MyTestConfiguration {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
And the MyService is something like
#Service
public class MyService {
#Autowired
private MyDataSource myDatasource;
...
}
Because MyService as an Autowired property MyDataSource, the context isn't initialized because it doesn't find any MyDataSource type for satisfying the #Autowired annotation of MyService. But why does it ever try to resolve this annotation? Is this is a mock?
Mockito does use cglib to create a new child class of MyService (and override all methods with mock methods).
But still, the dependencies of the parent will be injected, because this is how Spring does it's job:
if you have a parent class with some #Autowired fields, and a child class that inherits from this parent class, then Spring will inject the #Autowired fields of the parent when instantiating the child. I guess it's the same behavior in your case.
If you use an interface for MyService, then your problem will be solved.
If it's supposed to be a unit test (and not an integration test) you don't even need to use Spring, you can do it all with JUnit+Mockito. Rather than #Autowireing dependencies from Spring context, you can simply create mocks of the support objects (via #Mock) and inject them to the testee (via #InjectMocks). I believe your code could be simplified to something (conceptually) like this:
#RunWith(MockitoJUnitRunner.class)
public class ContentActionWebServiceControllerTest {
#Mock
private Service mockServiceUsedByController;
#InjectMocks
private YourController testee;
#Test
public void getRequestActionList() throws Exception {
assertFalse(testee.getRequestActionList().isEmpty());
// etc.
}
}

Categories