SpringBoot Junit bean autowire - java

I try to write junit test to my service.
I use in my project spring-boot 1.5.1. Everything works fine, but when I try to autowire bean (created in AppConfig.class), it gives me NullPointerException. I've tried almost everything.
This is my configuration class:
#Configuration
public class AppConfig {
#Bean
public DozerBeanMapper mapper(){
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.setCustomFieldMapper(new CustomMapper());
return mapper;
}
}
and my test class:
#SpringBootTest
public class LottoClientServiceImplTest {
#Mock
SoapServiceBindingStub soapServiceBindingStub;
#Mock
LottoClient lottoClient;
#InjectMocks
LottoClientServiceImpl lottoClientService;
#Autowired
DozerBeanMapper mapper;
#Before
public void setUp() throws Exception {
initMocks(this);
when(lottoClient.soapService()).thenReturn(soapServiceBindingStub);
}
#Test
public void getLastResults() throws Exception {
RespLastWyniki expected = Fake.generateFakeLastWynikiResponse();
when(lottoClient.soapService().getLastWyniki(anyString())).thenReturn(expected);
LastResults actual = lottoClientService.getLastResults();
Can someone tells me what's wrong ?
Error log:
java.lang.NullPointerException
at pl.lotto.service.LottoClientServiceImpl.getLastResults(LottoClientServiceImpl.java:26)
at pl.lotto.service.LottoClientServiceImplTest.getLastResults(LottoClientServiceImplTest.java:45)
and this is my service:
#Service
public class LottoClientServiceImpl implements LottoClientServiceInterface {
#Autowired
LottoClient lottoClient;
#Autowired
DozerBeanMapper mapper;
#Override
public LastResults getLastResults() {
try {
RespLastWyniki wyniki = lottoClient.soapService().getLastWyniki(new Date().toString());
LastResults result = mapper.map(wyniki, LastResults.class);
return result;
} catch (RemoteException e) {
throw new GettingDataError();
}
}

Ofcourse your dependency will be null,due to the #InjectMocks you are creating a new instance, outside the visibility of Spring and as such nothing will be auto wired.
Spring Boot has extensive testing support and also for replacing beans with mocks, see the testing section of the Spring Boot reference guide.
To fix it work with the framework instead of around it.
Replace #Mock with #MockBean
Replace #InjectMocks with #Autowired
Remove your setup method
Also apparently you only need a mock for the SOAP stub (so not sure what you need to mock for the LottoClient for).
Something like this should do the trick.
#SpringBootTest
public class LottoClientServiceImplTest {
#MockBean
SoapServiceBindingStub soapServiceBindingStub;
#Autowired
LottoClientServiceImpl lottoClientService;
#Test
public void getLastResults() throws Exception {
RespLastWyniki expected = Fake.generateFakeLastWynikiResponse();
when(soapServiceBindingStub.getLastWyniki(anyString())).thenReturn(expected);
LastResults actual = lottoClientService.getLastResults();

Related

Retryable annotation - Junit5 - Mockito - is it possible

Is it possible to write unit test using Junit 5 mockito for retryable annotations?
I am having a service interface which has only one method, which downloads the file from remote url
#service
interface downloadpdf{
#Retryable(value = { FileNotFoundException.class, HttpClientErrorException.class }, maxAttempts = 5, backoff = #Backoff(delay = 1000))
public string downloadpdffile(string remoteurl, string pdfname);
}
I have tried referring sites and found using Spring4JunitRunner implementation to test retry. Got confused with implementation. Is it possible to write unit test using Junit 5 mockito for retryable annotations?. Could you please elaborate on the solution here?
You need to use #SpringJUnitConfig (which is the equivalent of the JUnit4 runner). Or #SpringBootTest as you are using Boot.
#Retryable only works with beans managed by Spring - it wraps the bean in a proxy.
#SpringBootApplication
#EnableRetry
public class So71849077Application {
public static void main(String[] args) {
SpringApplication.run(So71849077Application.class, args);
}
}
#Component
class RetryableClass {
private SomeService service;
void setService(SomeService service) {
this.service = service;
}
#Retryable
void retryableMethod(String in) {
service.callme();
throw new RuntimeException();
}
#Recover
void recover(Exception ex, String in) {
service.failed();
}
}
interface SomeService {
void callme();
void failed();
}
#SpringBootTest
class So71849077ApplicationTests {
#MockBean
SomeService service;
#Test
void testRetry(#Autowired RetryableClass retryable) {
SomeService service = mock(SomeService.class);
retryable.setService(service);
retryable.retryableMethod("foo");
verify(service, times(3)).callme();
verify(service).failed();
}
}
I was also trying to implement this using Junit5.
Tried various options but that didn't help. Then after googling for few hours, got the following link and it helped to succeed.
https://doctorjw.wordpress.com/2022/04/29/spring-testing-a-single-bean-in-junit-5-springextension/
Reference code below, for detailed explanation, please refer the blog.
#Component
public class MyClass {
private ObjectMapper objectMapper;
private RestTemplate restTemplate;
#Value("${testValue:5}")
private int value;
#Retryable(....)
public void doStuff() throws SomeException {
...
}
}
What I’ve discovered is, if I declare my test class this way:
#ExtendWith( SpringExtension.class )
#Import( { MyClass.class, ObjectMapper.class } )
#EnableRetry
public class MyClassTest {
#Autowired
private MyClass myClass;
#MockBean
private RestTemplate restTemplate;
#Autowired
private ObjectMapper objectMapper;
#BeforeEach
public void setup() {
// If we are going to jack with the object configuration,
// we need to do so on the actual object, not the Spring proxy.
// So, use AopTestUtils to get around the proxy to the actual obj.
TestingUtils.setFieldValue( AopTestUtils.getTargetObject( myClass ), "value", 10 );
}
}
You will notice the inclusion of 1 other class, TestingUtils.class. This class looks like:
public class TestingUtils {
public static void setFieldValue( Object object, String fieldName, Object value ) {
Field field = ReflectionUtils.findField( object.getClass(), fieldName );
ReflectionUtils.makeAccessible( field );
ReflectionUtils.setField( field, object, value );
}
}
All credits goes to the author of the blog.

Error running JUnitWeb Test on Method setup

I have a Spring application,
and I've created this test:
#RunWith(SpringRunner.class)
#SpringJUnitWebConfig(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:backoffice-servlet.xml"
})
public class UserControllerTests {
#Autowired
private MockMvc mockMvc;
#Before
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
..
}
but when I start the test I got this error:
rg.junit.runners.model.InvalidTestClassError: Invalid test class 'com.pastis.UserControllerTests':
1. Method setup() should be public
2. Method setup should have no parameters
Here an example:
RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = MyWebConfig.class)
public class CustomerControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup () {
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
#Test
public void testUserController () throws Exception {
ResultMatcher ok = MockMvcResultMatchers.status()
.isOk();
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/customers");
this.mockMvc.perform(builder)
.andExpect(ok);
}
}
So I explain the output of the exception:
Add the modifier public to your method setup, otherwise JUnit can't invoke it
Remove the parameter from the method, #Before, #After and such don't allow parameters.
How to setup MockMvc correctly is another question. Recent Spring and additional annotations regarding web scope initialization and -behavior leave the scope of this answer. (This also requires more clarification, for example which JDK, which Spring or Spring Boot… XML configuration, dbunit and JUnit 4 suggest a legacy context.)

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: while running tests

trying to run a test but end up in the error in title. i am working in a springboot project. the project works fine without issues. when i try to run the test, it is throwing the error.
added the dependent ValidationService class in the test folder
inside java package
#Service
public class ValidationService {
#Autowired
TicketDetailRepository ticketDetailRepository;
public boolean isValidTicket(Long ticketId)
{
Optional<TicketDetailEntity> optionalTicketDetailEntity = ticketDetailRepository.findById(ticketId);
if(optionalTicketDetailEntity.isPresent())
{
return optionalTicketDetailEntity.get().getTicketStatus();
}
else
{
return false;
}
}
}
#RestController
#RequestMapping(path = "/xxx")
public class ValidationController {
#Autowired
ValidationService validationService;
#RequestMapping("validate")
public ValidationResponse validate(#RequestBody #Valid ValidationRequest validationRequest) {
return true;
}
}
inside test package
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = { ValidationController.class }, secure = false)
#Import(ExceptionHandlerConfiguration.class)
public class ValidationControllerIntegrationTest {
#Autowired
MockMvc mockMvc;
#Autowired
ObjectMapper objectMapper;
#Autowired
RequestMappingHandlerAdapter adapter;
#Test
public void xxx() throws Exception {
ValidationRequest validationRequest = ValidationRequest.builder().build();
jsonPost("xxx", validationRequest)
.andExpect(status().isOk())
.andExpect(jsonPath("xxx").value(true))
.andExpect(status().isOk())
.andExpect(jsonPath("xxx").value(false));
}
ResultActions jsonPost(String url, Object entity) throws Exception {
return
this.mockMvc.perform(MockMvcRequestBuilders
.post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(entity))
)
.andDo(MockMvcResultHandlers.print());
}
}
Expected : Should not throw error
Actual : throws unsatisfied dependency error while running test.
You should provide the full error. It is not clear which bean is not initialized.
One suggestion is to add #ComponentScan on your test class for ValidationService.
like
#ComponentScan ({"PACKAGE_NAME_WHERE_ValidationService_IS_Resides"})
You need mock the Service layer code. You can use #MockBean annotation.
#MockBean
private ValidationService service;
Example Integration Test1
Example Integration Test2

Injecting mocks with Mockito does not work

I'm using Mockito to test my Spring project, but the #InjectMocks seems not working in injecting a mocked service into another Spring service(bean).
Here is my Spring service that I want to test:
#Service
public class CreateMailboxService {
#Autowired UserInfoService mUserInfoService; // this should be mocked
#Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
}
And below is the service that I want to mock:
#Service
public class UserInfoService {
public String getData() {
return "original text";
}
}
My test code is here:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
#Autowired
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void deleteWithPermission() {
when(mUserInfoService.getData()).thenReturn("mocked text");
System.out.println("test 1: " + mUserInfoService.getData());
mCreateMailboxService.createMailbox();
}
}
but the result would like
test 1: mocked text
test 2: original text // I want this be "mocked text", too
it seems that the CreateMailboxService didn't get the mocked UserInfoService but using Spring's autowired bean.
Why is my #InjectMocks not working?
In my case, I had a similar issue when I worked with JUnit5
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
...
#InjectMocks
MyService underTest;
#Test
void myMethodTest() {
...
}
underTest was null.
The cause of the problem was that I used #Test from JUnit4 package import org.junit.Test; instead JUnit5 import org.junit.jupiter.api.Test;
For those who stumbles on this thread and are running with JUnit 5 you need to replace
#RunWith(SpringJUnit4ClassRunner.class)
with
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class)
Further reading here. Unfortunately there is no hint when executing the test cases with JUnit 5 using the old annotation.
You can create package level setter for mUserInfoService in CreateMailboxService class.
#Service
public class CreateMailboxService {
#Autowired UserInfoService mUserInfoService; // this should be mocked
#Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
void setUserInfoService(UserInfoService mUserInfoService) {
this.mUserInfoService = mUserInfoService;
}
}
Then, you can inject that mock in the test using the setter.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mCreateMailboxService = new CreateMailboxService();
mCreateMailboxService.setUserInfoService(mUserInfoService);
}
...
}
This way you can avoid problems with #InjectMocks and Spring annotations.
If you are trying to use the #Mock annotation for a test that relies directly on Spring injection, you may need to replace #Mock with #MockBean #Inject (both annotations), and #InjectMocks with #Inject. Using your example:
#MockBean
#Inject
UserInfoService mUserInfoService;
#Inject
CreateMailboxService mCreateMailboxService;
I had a pretty similar situation. I am writing it down just in case any reader is going through the same. In my case I found that the problem was that I was setting my injected variable as final in the local class.
Following your example, I had things like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
CreateMailboxService mCreateMailboxService = new CreateMailboxService(mUserInfoService);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void deleteWithPermission() {
...
}
}
But in this class I had it like this:
#Service
public class CreateMailboxService {
private final UserInfoService mUserInfoService; // it is NOT injecting Mocks just because it is final! (all ok with private)
private final LogicService mLogicService; // it is NOT injecting Mocks just because it is final! (all ok with private)
#Autowired
public CreateMailboxService(UserInfoService mUserInfoService, LogicService mLogicService) {
this.mUserInfoService = mUserInfoService;
this.mLogicService = mLogicService;
}
public void createMailbox() {
...
}
}
Just deleting the final condition, #InjectMocks problem was solved.
For those who are running with JUnit 5 you need to replace the #RunWith(SpringJUnit4ClassRunner.class) with #ExtendWith(MockitoExtension.class).
For further reading take a look here.
there is no need of #Autowired annotation when you inject in the test class. And use the mock for the method to get your mocked response as the way you did for UserInfoService.That will be something like below.
Mockito.when(mCreateMailboxService. getData()).thenReturn("my response");
You can use MockitoJUnitRunner to mock in unit tests.
Use #Mock annotations over classes whose behavior you want to mock.
Use #InjectMocks over the class you are testing.
Its a bad practice to use new and initialize classes (better to go for dependency injection) or to introduce setters for your injections. Using setter injection to set dependencies only for tests is wrong as production code should never be altered for tests.
#RunWith(MockitoJUnitRunner.class)
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
...
}

Mock object method call using Spring Boot and Mockito

I am trying to write a test for this Java SpringBoot's class:
https://github.com/callistaenterprise/blog-microservices/blob/master/microservices/composite/product-composite-service/src/main/java/se/callista/microservices/composite/product/service/ProductCompositeIntegration.java
Specifically, I am trying to "mock" this method call:
URI uri = util.getServiceUrl("product");
I figured out I should "mock" the ServiceUtils object in order to do this. I tried this using the #Mock and #InjectMocks annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
public class ProductCompositeIntegrationTest {
#InjectMocks
#Autowired
private ProductCompositeIntegration productIntegration;
#Autowired
private RestTemplate restTemplate;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void myTest() {
Mockito.when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
ResponseEntity<Iterable<Product>> products = productIntegration.getAllProducts();
}
}
But this way it still calls the original ServiceUtils object, and not the "mocked" one. Also tried without the #Autowired annotation at the ProductCompositeIntegration, but this results in a NullPointerException.
What am I doing wrong?
My main class looks like this:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
public class ProductCompositeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductCompositeServiceApplication.class, args);
}
}
The ServiceUtils object that I am trying to mock is specified in a class, annotated with Spring's #Component annotation to inject it into the other classes using #Autowired.
After a lot of trial and error I managed to solve this problem.
I dropped the
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
annotations aboved the test class.
I marked the class that I was testing with #InjectMocks and the dependencies with #Mock:
public class ProductCompositeIntegrationTest {
#InjectMocks
private ProductCompositeIntegration productIntegration;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
private RestTemplate restTemplate = new RestTemplate();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
productIntegration.setRestTemplate(restTemplate);
}
#Test
public void someTests() {
when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
//Test code...
}
}
I'm not sure if this is the best approach ("the Spring way"), but this worked for me.
This article made it all clear to me: http://rdafbn.blogspot.be/2014/01/testing-spring-components-with-mockito.html
You have to write a FactoryBean like
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
In your test-context.xml you have to add the following lines.
<bean id="serviceUtilMock" class="MockitoFactoryBean">
<constructor-arg value="your.package.ServiceUtil" />
</bean>
If you don't use XML configuration, then you have to add the equivalent to above in your Java configuration.

Categories

Resources