Error with Zipkin in unit test (cannot mock Span.baggageItems) - spring

I am getting this error when I try to run the unitTest in my spring boot application.
java.lang.NullPointerException: null
at org.springframework.cloud.sleuth.Span.baggageItems(Span.java:381)
at org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector.inject(ZipkinHttpSpanInjector.java:27)
at org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector.inject(ZipkinHttpSpanInjector.java:15)
at org.springframework.cloud.sleuth.instrument.web.client.AbstractTraceHttpRequestInterceptor.publishStartEvent(AbstractTraceHttpRequestInterceptor.java:60)
at org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptor.intercept(TraceRestTemplateInterceptor.java:52)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
I notice that I only get this error when I use this version for spring-cloud-dependencies:
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
but if I use this previous version:
<spring-cloud.version>Camden.SR6</spring-cloud.version>
everything works as I expect
What can I do in order to avoid this error with the last version?
The unit tests are extended this class in order to mock the SpanAccessor
public class AbstractSpanAccessorTest {
#MockBean
private Tracer tracer;
#MockBean
private Span span;
private Random random = new Random();
#Before
public void mockSpan() {
long id = createId();
Span spanMock = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(spanMock.traceIdString()).when(span).traceIdString();
doReturn(span).when(tracer).getCurrentSpan();
doReturn(span).when(tracer).createSpan(anyString());
}
private long createId() {
return random.nextLong();
}
}

Why are you mocking a span? This makes absolutely no sense. Also a Span is never a bean. You already create a normal span via a builder and you should leave that. Assuming that you have set up the Boot context property and you want to mock out tracer bean you should do the following
public class AbstractSpanAccessorTest {
#MockBean
private Tracer tracer;
private Random random = new Random();
#Before
public void mockSpan() {
long id = createId();
Span spanMock = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(spanMock).when(tracer).getCurrentSpan();
doReturn(spanMock).when(tracer).createSpan(anyString());
}
private long createId() {
return random.nextLong();
}
}

Related

SonarQube doesn't recognize Mapper's unit test

Running SonarQube Version 6.7.2 (build 37468) - LGPL v3, Junit 4 and mockito in our project, I noted that SonarQube does not recognize Mapper's unit test and decrease the project's percentual. In my local enviroment, the test works well and the coverage is 100% by Eclipse.
Below is the UT code:
#RunWith(MockitoJUnitRunner.class)
public class ClassMapperTest {
#Mock
private ClassMapper mapper;
#Mock
private ClassDTO dto;
#Before
public void setUp() {
mapper = Mockito.mock(ClassMapper.class);
dto = Mockito.mock(ClassDTO.class);
}
#Test
public void returnResource() {
Mockito.when(mapper.fromMethod(Mockito.anySet())).thenReturn(new HashSet<>());
mapper.fromMethod(new HashSet<ClassDTO>());
}
The statistics:
After Commit:
Does anyone have any idea?
Sonarqube is right with the computation. You do have a major issue within your test, the code you seem to be testing is mocked aka you are not testing the actual code, but a fake of it.
When you mock a class you create a dummy fake version of this class, which does not have any implementation (mapper = Mockito.mock(ClassMapper.class);).
you then tell your mock to return a value when a method is called Mockito.when(mapper.fromMethod(Mockito.anySet())).thenReturn(new HashSet<>());. This way you are actually not testing your fromMethod, you just testing a method, which you told in your test what to return.
A proper test would look something like this:
#RunWith(MockitoJUnitRunner.class)
public class ClassMapperTest {
private ClassMapper mapper;
#Mock
private ClassDTO dto;
#Before
public void setUp() {
mapper = new ClassMapper();
// no need to do that, MockitoJUnitRunner is doing this for you
// dto = Mockito.mock(ClassDTO.class);
}
#Test
public void returnResource() {
// calling the actual method
assertTrue(mapper.fromMethod(new HashSet<ClassDTO>()) != null);
}
}
There is also no need for the dto as it is not used within your test at all, but I left it in there, to mark the unnecessary mock instantiation, which is done by the MockitoJUnitRunner.
// Disclaimer: I am not guaranteeing that your tests will pass, with my suggestion, I only want to highlight the problem with the test.

Dependency injection with mockito example

I am very new with Mockito and I don't get the following example (classes were provided, only test to write) and how to solve it.
What I try to do is use a test double for the supplier so that we can control the returned greeting in the test and assert that the GreetingService does not modify the greeting message in any way. Then assert that the returned greeting string is equal to "Hello Andy.".
public class Greeting {
private final String template;
public Greeting(String template) {
this.template = template;
}
public String forName(String world) {
return String.format(template, world);
}
}
#Component
public class GreetingService {
private final Supplier<Greeting> greetingSupplier;
public GreetingService(Supplier<Greeting> greetingSupplier) {
this.greetingSupplier = greetingSupplier;
}
public String greet(String name) {
return greetingSupplier.get().forName(name);
}
}
#Component
public class RandomGreetingSupplier implements Supplier<Greeting> {
private final List<Greeting> greetings = Arrays.asList(
new Greeting("Hello %s."),
new Greeting("Hi %s!"),
);
private final Random random = new Random();
#Override
public Greeting get() {
return greetings.get(random.nextInt(greetings.size()));
}
}
#SpringBootTest
public class GreetingServiceTest {
#Autowired
GreetingService greetingService;
#MockBean
Supplier<Greeting> greetingSupplier;
#Test
void getGreetingForPerson() {
String name = "Andy";
// that test cannot know which greeting will be returned by the supplier
// WHY IS IT NULLPOINTEREXCEPTION AFTER INITIALIZING #MockBean
//String greeting = greetingService.greet(name);
//assertThat(greeting).contains(name);
// WROTE SUCH TEST HERE -> NullPointerException WHY?
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
assertThat(greetingSupplier.equals("Hello Andy."));
// THIS IS WORKING & TEST PASSED BUT I GUESS ITS WRONG?
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
assertThat(greetingSupplier.equals("Hello Andy."));
}
}
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You can't chain calls like that, you need to produce intermediate results, like
Supplier<Greeting> supplier = mock(Supplier.class);
Mockito.when(supplier).forName().thenReturn("Hello %s.");
Mockito.when(greetingSupplier.get()).thenReturn(supplier);
For dependency injection, you need to create the subject under test with the mocked Supplier. You can do that in a #Before method for example.
Your mocking is wrong.
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You mocked Supplier<Greeting> and the default behavior is to return null. So when you call greetingSupplier.get() in your first line it returns null. You directly chain forName which nou basicall is null.forName which leads to an error.
Your second part is actually (kind of) correct.
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
You now properly return a response from greetingSupplier.get(). Instead of chaining the call.
However I would argue that your excercise is wrong. Why? When using a Supplier<?> in Spring it actually is a lazy beanFactory.getBean call. You can lazily inject dependencies this way. You should have a mock for Greeting which returns a hardcoded String which you can check.

JUnit test not loading properties file, but runtime code works correctly

I have some production code that works correctly. But the JUnit test I wrote for it does not load the testing properties file and fails.
Googled for examples of how to load test properties. None of the answers helped.
The testing properties file, stored in src/test/resources, is:
#Generated by Git-Commit-Id-Plugin
#Sat Aug 31 08:34:13 EDT 2019
build-details.branch=feature/issue-189
build-details.build.time=20190831083412
build-details.build.version=0.4.9
build-details.commit.id=3cbdbaee42d2b6bde342fce32ecd61905d8d12d4
build-details.commit.time=20190831083101
The JUnit test is:
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource(locations = {"classpath:/build-details.properties"})
public class DetailsServiceTest {
private static final String TEST_BRANCH = "feature/issue-532";
private static final String TEST_BUILD_TIME = "20190831083412";
private static final String TEST_BUILD_VERSION = "0.4.9";
private static final String TEST_COMMIT_ID = "3cbdbaee42d2b6bde342fce32ecd61905d8d12d4";
private static final String TEST_COMMIT_TIME = "20190831083101";
#InjectMocks private DetailsService detailsService;
#Test
public void testGetDetails() {
final BuildDetails result = detailsService.getBuildDetails();
assertNotNull(result);
assertEquals(TEST_BRANCH,
result.getBranch());
assertEquals(TEST_BUILD_TIME,
result.getBuildTime());
assertEquals(TEST_BUILD_VERSION,
result.getBuildVersion());
assertEquals(TEST_COMMIT_ID,
result.getCommitId());
assertEquals(TEST_COMMIT_TIME,
result.getCommitTime());
}
}
And the service being tested is:
#Service
#PropertySource({"classpath:/build-details.properties"})
public class DetailsService {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
#Value("${build-details.branch") private String branch;
#Value("${build-details.build.time}") private String buildTime;
#Value("${build-details.build.version}") private String buildVersion;
#Value("${build-details.commit.id}") private String commitId;
#Value("${build-details.commit.time}") private String commitTime;
public BuildDetails getBuildDetails() {
final BuildDetails result = new BuildDetails();
result.setBranch(branch);
result.setBuildTime(buildTime);
result.setBuildVersion(buildTime);
result.setCommitId(commitId);
result.setCommitTime(commitTime);
return result;
}
}
As I mentioned before, the service works as expected. When I run the service and query it, I'm getting back:
{"branch":"${build-details.branch","buildTime":"20190831112809","buildVersion":"20190831112809","commitId":"6e709c0401627149505c91348f960e585fc5f24b","commitTime":"20190831103506"}
(values reflecting the live build).
But when I run the unit test, all of the field values are consistently null.
Using the MockitoJUnitRunner doesn't load the Spring context in the test, so those value annotations aren't getting the values injected. You could try switching to a different runner that loads the spring context to have spring inject those values from your test properties file.
Try using the Spring runner to see if that helps. Instead of using #InjectMocks you can use #Autowired to get the bean from spring which should have the values from the test properties file.
#RunWith(SpringRunner.class)
#TestPropertySource(locations = {"classpath:/build-details.properties"})
public class DetailsServiceTest {
#Autowired
private DetailsService detailsService;
//...
}
Also, it looks like you're missing a closing curly brace on one of the #Value annotations and a setter is using the build time. Guessing it should be:
#Value("${build-details.branch}") private String branch;
// ...
result.setBuildVersion(buildVersion); // instead of buildTime

Value of doReturn not used

I use spring boot 2.
I search to test a private method in the facade
#RunWith(SpringRunner.class)
#SpringBootTest
public class SamplingsFacadeTest {
#Autowired
private SamplingsFacade facade;
#MockBean
private SamplingsService samplingsService;
#Test
public void exampleTest() throws Exception {
List<Samplings> samplings = new ArrayList<>();
Samplling sampling = new Samplings();
..
samplings.add(sampling);
//fake call
Mockito.doReturn(samplings).when(samplingsService.getSamplingContainingNonCompliantTest());
//real call
List<FactoryEmailNCDto> factoryEmails = Whitebox.invokeMethod(facade, "prepareDataNoncompliantSampling");
}
public List<Samplings> getSamplingContainingNonCompliantTest() {
return samplingsRepository.findSamplingContainingNonCompliantTest();
}
In Facade In
private List<FactoryEmailNCDto> prepareDataNoncompliantSampling() {
List<FactoryEmailNCDto> factoryEmailNC = new ArrayList<>();
List<Samplings> samplings = samplingsService.getSamplingContainingNonCompliantTest();
for (Samplings sampling : samplings) {
...
}
}
Why when I debug, samplings is null and not the value I created
Mockito.doReturn(samplings)
.when(samplingsService.getSamplingContainingNonCompliantTest());
One potential problem is that doReturn takes the form doReturn(value).when(mock).method(), not doReturn(value).when(mock.method()). The latter is considered an incomplete stubbing. Instead, it should look like this:
Mockito.doReturn(samplings)
.when(samplingsService)
.getSamplingContainingNonCompliantTest();
Note that there may be other problems with your test; the code you've written expects samplingsService to be public and non-final, and your getSamplingContainingNonCompliantTest() to likewise be public, non-static, and non-final, but the code sample you have does not confirm that. You may want to call Mockito.validateMockitoUsage() in an #After method, or use a #Rule that handles Mockito annotations and cleanup for you.

spring cloud is failing when I try to mock sleuth in unit tests

I am facing some error when I try to mock objects like Tracer and Span in unit tests if I use Dalston.SR3 or Dalston.Release versions, but this problem doesn't happen if I use Camden.SR6 or Camden.SR7 versions.
Find an example code here
Microservice msvc-a is using Dalston version and has two test classes, where only is failing the class where I am trying to mock the Tracer and the Span objects.
Microservice msvc-b is using Camden version and has the same test classes.
At the same time, I can't understand this situation when I am in debug mode in STS and why I can't see any error trace or something like that... only a NullpointerException.
public class AbstractSpanAccessorTest {
#MockBean
private Tracer tracer;
#MockBean
private Span span;
private Random random = new Random();
#Before
public void mockSpan() {
long id = createId();
Span spanMock = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(spanMock.traceIdString()).when(span).traceIdString();
doReturn(span).when(tracer).getCurrentSpan();
doReturn(span).when(tracer).createSpan(anyString());
}
private long createId() {
return random.nextLong();
}
}
It was my mistake. The correct way to mock the Span is:
#Before
public void mockSpan() {
long id = createId();
span = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(span).when(tracer).getCurrentSpan();
doReturn(span).when(tracer).createSpan(anyString());
}
Tracer wouldn't mock at all with Finchley.SR2 so I ended up with this:
Tracing tracing = Tracing.newBuilder().build();
Tracer tracer = tracing.tracer();

Resources