Getting NullPointerException tracer.currentSpan() is Null - java

I'm trying to create test case for given class and want to return traceId value but getting trace.currentSpan() is null.
Here is my Class
public class ConsumerService{
private final Tracer tracer;
#Autowired
private RequestService requestService;
public void consumerProductionRequest(DisclaimerRequest disclaimerRequest){
String traceId=tracer.currentSpan.context().traceId();
log.info(traceId);
if(disclaimerRequest.getReqId()==null){
disclaimerRequest.setReqId(UUID.randomUUID().toString());
}
}
}
//Test Class
#ExtendWith(MockitoExtension.class)
class ConsumerServiceTest{
#InjectMocks
ConsumerService consumerService;
#Autowired
Tracer tracer;
#Test
void Test(){
Tracer tracer=Mockito.mock(Tracer.class);
String traceId;
Mockito.when(tracer.currentSpan().context.traceId()).thenReturn(traceId);
DisclaimerRequest disclaimerRequest=new DisclaimerRequest();
consumerService.consumerProductionRequest(disclaimerRequest);
}
}
Why am I getting tracer.currentSpan() null. I'm using JUnit 5 and new in this. Please someone help me to solve this.

Here, in your test class:
#Autowired
Tracer tracer;
Turn that into
#Mock
Tracer tracer;
(and add the corresponding import to Mockito). You want that Mockito creates a mock object, and then inserts that into your object under test. Also note that your test method itself redefines tracer, thus shadowing whatever else you did on class level. Therefore that spec for tracer within the test method won't have any effect on the mocked Tracer instance that already got injected into your object under test.

Tracer tracer=Mockito.mock(Tracer.class);
String traceId;
Mockito.when(tracer.currentSpan().context.traceId()).thenReturn(traceId);
tracer will be mock and you can see it in debug or just simply print it.
also each method returns null because you didn't specify which method will return value because spring context does not work in unit tests. In integration tests you can autowire it.
Mockito.when(tracer.getCurrentSpan()).thenReturn(someSpan);
Mockito.when(tracer.getCurrentSpan().getContext()).thenReturn(someContext);
String traceId;
Mockito.when(tracer.currentSpan().context.traceId()).thenReturn(traceId);
You have to mock getCurrentSpan and getContext methods that will solve your problem. Also you can not autowire it just use #Mock annotation. Spring context does not exist in unit tests so it won't be autowired. Spring related codes won't work. Integration test runs the spring context for you and you can autowire it.

Related

Spring autowired dependency is null in class being tested

I'm trying to write a unit test for my PersonController class, which uses a dependency org.modelmapper.ModelMapper. The problem is that when the test calls a method of PersonController that uses the ModelMapper, the ModelMapper is null which results in an error. Clearly it is not being autowired correctly when running the test, but it works when running the application. I am hoping there is a way to fix this problem without needing to add the ModelMapper as a constructor parameter.
The code snippets are shown below:
#SpringBootTest
class PersonControllerTest {
#Mock
private IPersonService personService;
#InjectMocks
private PersonController underTest;
#BeforeEach
void setUp() {
underTest = new PersonController(personService);
}
...Tests...
}
And the relevant PersonController code is:
public class PersonController {
#Autowired
private ModelMapper _modelMapper;
private final IPersonService _personService;
public PersonController (IPersonService personService) {
_personService = personService;
}
...Code that calls _modelMapper...
}
One problem here is that you are creating a controller class manually using new PersonController(personService) so spring annotation #Autowired does not have any effect here (because spring is not creating the class is not configuring it).
Another issue is that you are using mockito #InjectMocks which basically injects mocks (for the unit test) but at the same time, you are using #SpringBootTest which is for integration test (application is started as normal but in test mode).
Based on your description is not clear to me if you are trying to code a unit test or an integration test. so:
If you are coding a unit test, remove #SpringBootTest add ModelMapper as a constructor parameter and instantiate a class with two mocks.
If you are coding an spring integration test, remove #InjectMocks and manual class instantiation of class and flag controller as #Autowire in test.
First of all you should annotate your class PersonControllerTest with
#ExtendWith(SpringExtension.class)
class PersonControllerTest {
or
#ExtendWith(MockitoExtension.class)
class PersonControllerTest {
depending on your use case. This should already fix your problem. If you are still using Junit4 you should use
#RunWith(Springrunner.class)
class PersonControllerTest {
but since you are using
#BeforeEach
that should not be the case :)
EDIT:
When you use ModelMapper via #Autowired you have to use #ExtendWith(SpringExtension.class) . You also have to annotate your class with #SpringBootTest or import the classes that should be available via dependency Injection manually using the #Import annotation.

Mock JWT Utils to validate Token

I want to create JUnkt test for this endpoint:
#Autowired
private JwtTokenProvider jwtTokenProvider;
#PostMapping("reset_token")
public ResponseEntity<?> resetToken(#Valid #RequestBody ResetPasswordTokenDTO resetPasswordTokenDTO, BindingResult bindResult) {
final String login = jwtTokenProvider.getUsername(resetPasswordTokenDTO.getResetPasswordToken());
}
Full code: Github
JUnit test:
#Test
public void resetTokenTest_NOT_FOUND() throws Exception {
when(usersService.findByResetPasswordToken(anyString())).thenReturn(Optional.empty());
mockMvc.perform(post("/users/reset_token")
.contentType(MediaType.APPLICATION_JSON)
.content(ResetPasswordTokenDTO))
.andExpect(status().isNotFound());
}
I get NPE at this line when I run the code:
final String login = jwtTokenProvider.getUsername(resetPasswordTokenDTO.getResetPasswordToken());
How I can mock jwtTokenProvider properly? As you can see I have a file with test data which I load but the token is not extracted. Do you know how I can fix this issue?
The most straightforward way is to use Mockito and create mock instances and pass it directly to your controller class using constructor injection.
However, if you do not wish to use constructor injection (I recommend you to use it though, as it is much more explicit) you need to define your beans in a separate test configuration class
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
public JwtTokenProvider mockJwtTokenProvider() {
return Mockito.mock(JwtTokenProvider.class);
}
}
Also, add the correct profile to your test class by #ActiveProfiles("test")
You can consider using a #MockBean directly in your test class to mock your JwtTokenProvider. #MockBean annotation is Spring-ish and is included in spring-boot-starter-test. The Spring Boot documentation summarizes it well:
Spring Boot includes a #MockBean annotation that can be used to define
a Mockito mock for a bean inside your ApplicationContext. You can use
the annotation to add new beans or replace a single existing bean
definition. The annotation can be used directly on test classes, on
fields within your test, or on #Configuration classes and fields. When
used on a field, the instance of the created mock is also injected.
Mock beans are automatically reset after each test method.
The #MockBean annotation will make Spring look for an existing single bean of type JwtTokenProvider in its application context. If it exists, the mock will replace that bean, and if it does not exist, it adds the new mock in the application context.
Your test class would look like this:
import org.springframework.boot.test.mock.mockito.MockBean;
#MockBean
#Qualifier("xxx") //If there is more than one bean of type JwtTokenProvider
private JwtTokenProvider jwtTokenProvider;
#Test
public void resetTokenTest_NOT_FOUND() throws Exception {
when(jwtTokenProvider.getUsername(anyString())).thenReturn(Optional.empty());
mockMvc.perform(post("/users/reset_token")
.contentType(MediaType.APPLICATION_JSON)
.content(ResetPasswordTokenDTO))
.andExpect(status().isNotFound());
}
You might also want to check this and this.

Junit method invocation fails due to spring injection

I have written Junit test class to test particular method. One of the variables being processed in this method is spring injected, by taking the value from properties file.
Below is my test method
#Test
public void myTestMethod() {
//invoking the method to be tested
Assert.assertTrue(updateGroceries());
}
This is the class to be tested,
public class ToBeTested {
//Spring injected value
String categories;
public boolean updateGroceries() {
List<String> categoryList = StringUtils.convertStringToList(categories);
}
In the above class, categories variable is spring injected.
This is properties file content:
categories = Dals,Pulses,Dry Fruits,Edible Oil
Now while running my Junit method, execution is failing because dependency injection is failing.Since the code I want to test runs on tomcat. I want to test the code without running tomcat. Please suggest some solution.
First of all to run mockito you need to enable it over your test.
Using annotation #RunWith(MockitoJunitRunner.class) or execute at the beginning of your test Mockito.initMocks().
Then your test should look like:
#RunWith(MockitoJunitRunner.class)
private YourTest{
#InjectMocks
ToBeTested toBeTested;
#Mock
ToBeTestedDependency dependency;
#Before
public void setUp(){
ReflectionTestUtils.setField(toBeTested, "categories",
"someCategory");
}
#Test
public void shouldDoThisOrThat(){
toBeTested.updateCategories();
}
}
Unfortunately mockito doesn't support injecting #Valueannotated field. You need to use ReflectionTestUtils or setup run your test with SpringJUnit4ClassRunner where you need to define your spring context with PropertyPlaceholder configuration to resolve property that you have as your Value key. There you can find reference to documentation and example of spring testing approach.
Hope this helped.
You should look at Mockito. When you use mockito framework, you can create mocks for spring injected values. You should read more on mockito website.

#RefreshScope seems to ignore Mockito's mocks

I'm implementing a service using Spring Boot and Spring Cloud Config service to provide the configuration values. In my Service I have a couple of config values which need to refresh when the value changes in the remote Git repo, and I was using #RefreshScope to enable that feature.
The problem comes when I try to inject a mock for RestTemplate in that service, it appears to ignore it and use the autowired instance instead. If I comment out the annotation it seems to work fine.
Here's the code for the Service:
#Service
#RefreshScope
public class MyServiceImpl implements MyService {
private static final Logger LOG = Logger.getLogger(MyServiceImpl.class);
#Autowired
public RestTemplate restTemplate;
#Value("${opts.default}")
private String default;
#Value("${opts.address}")
private String address;
#Value("${opts.separator}")
private String separator;
...
}
Test source code:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class ServiceTest {
#Mock
private RestTemplate restTemplate;
#Autowired
#InjectMocks
private MyServiceImpl service;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
public void testMethod() throws Exception {
when(restTemplate.postForObject(anyString(), any(), eq(ServiceResponse.class), anyMap())).thenReturn(getSuccessfulResponse());
ServiceResponse response = service.doYourStuff();
Assert.assertNotNull(response);
Assert.assertTrue(response.isSuccessful());
}
...
}
When adding the #RefreshScope the bean becomes a proxy instead of an actual raw implementation. Currently the RestTemplate is set on the proxy rather then the underlying instance. (If you debug you would see that your MyServiceImpl is actually more like an instance of MyServiceImpl$SpringCgLib#353234).
To fix you need to manually set the dependency using ReflectionTestUtils and AopTestUtils. The latter is to obtain the actual proxy.
Remove the #InjectMocks annotation and add the following to your setup method after the initialization of the mocks:
Object actualTarget = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setfield(actualTarget, "restTemplate", restTemplate);
For versions earlier as 4.2 the following might do the trick
Object actualTarget = (service instanceof Advised) ? ((Advised) service).getTargetSource().getTarget() : service;
The problem is that Mockito doesn't detect the proxy and just sets the field. The ReflectionTestUtils doesn't detect the proxy either hence the manual unwrapping. I actually stepped into this trap a couple of times before, which led me to create SPR-14050 this morning to have it embedded in the ReflectionTestUtils to easy the pain a little.

#ReplaceWithMock with #Qualifier

I am using springockito-annotations 1.0.9 for integration testing.
I have the following controller:
#Autowired
public Controller(
#Qualifier("passwordService ") PasswordService passwordService ,
#Qualifier("validator") Validator validator,
#Qualifier("reportService") ReportService reportService,
DateCalculator dateCalculator,
Accessor accessor){
this.passwordService = passwordService;
this.validator = validator;
this.reportService = reportService;
this.dateCalculator = dateCalculator;
this.accessor = accessor;
}
In the test I am going to replace beans from context using #ReplaceWithMock annotation.
But unfortunatly it works only for dependencies whithout #Qualifier annotation.
Namely, my test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class, classes = {TestContext.class})
public class ControllerTest {
#Autowired
#ReplaceWithMock
private PasswordService passwordService ;
#Autowired
#ReplaceWithMock
private Validator validator;
#Autowired
#ReplaceWithMock
private ReportService reportService;
#Autowired
#ReplaceWithMock
private DateCalculator dateCalculator;
#Autowired
#ReplaceWithMock
private Accessor accessor;
#Autowired
private Controller controller;
}
In the last case after initializing context only DateCalculator and Accessor beans replacing correctly with needed mocks, but the another bean autowiring as normal beans from main context.
After debugging I have found that QualifierAnnotationAutowireCandidateResolver couldn't identify correctly bean. In the lines below beginning from 229:
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
Spring tried to extract qualifier from mocked dependency, but it is empty.
Will be good to know how I can correctly replace dependency with #Qualifier to mock object.
Edit: added a link to alternatives to whitebox, it disappears in later versions of Mockito
It is possible to use mockitos #Mock and #InjectMocks to inject things into your class that you want to test, as suggested in an other post. I used to think it was a great way to test spring managed beans, but now i think it is problematic; if the
injection done by #InjectMocks fails, it does so silently, and you don't know why. When creating the test it can be manageble, but when you have some tests like this, and several tests starts to fail with nullpointers because of a small unintentional
change to the application context that someone made, or after a merge that introduced a minor anomaly, or similar, it gets more confusing than it need to be.
I advise you to use mockitos Whitebox instead, see my example below. With it you can explicitly tell what field you want to "inject" with what object, and in complicated situations, you can inject into more then one object. Mockito uses Whitebox when injecting (but swallows all exceptions).
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration( /*something that fits your setup*/ )
public class ControllerTest {
#Autowired
private PasswordService passwordService ;
#Autowired
private Validator validator;
#Autowired
private ReportService reportService;
#Autowired
private Controller testObject;
#Before
public void setupBefore() {
//Since this "injection" is done manually the qualifiers does not matter
Whitebox.setInternalState(testObject, "passwordService", passwordService);
Whitebox.setInternalState(testObject, "validator", validator);
Whitebox.setInternalState(testObject, "reportService", reportService);
}
#Test
public void testSomething() {
}
}
If you are doing regular unit testing Whitebox can help to do testing without a spring context. I can highly recommend that approach (but it is a bit off topic, and I will not post the example I wrote before I noticed that you were doing integration tests ;) ).
Edit: If you are using later versions of Mockito you will notice that Whitebox has disappeared, so what to do instead?
I faced that situation, and asked for advice: What do I use instead of Whitebox in Mockito 2.2 to set fields?
You don't need to do it any more. Mockito itself starting from version 1.8.3 now support annotated mocks and mock injections as described here: http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/Mockito.html#21
We do now the following for our unit tests:
// No annotation required
public class SomeTest {
#Mock
private SomeDependency someDependency;
#Mock
private SomeDependency2 someDependency2;
#InjectMocks
private ClassUnderTest classUnderTest;
#BeforeMethod(alwaysRun = true)
public void setUp() {
MockitoAnnotations.initMocks(this);
}
public void testSomething() {
// Do your Mockito test here.
}
}

Categories

Resources