Mock a MDC data in spring boot test - spring-boot

I want to mock a data which is fetched from MDC in test class other wise when the code executed it return a null value. So i try below,
#RunWith(SpringRunner.class)
#SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK,classes = TestApp.class)
public class Test {
private MockMvc restMvc;
#Before
public void setUp() {
mock(MDC.class);
this.restMvc = MockMvcBuilders.standaloneSetup(TestController).build();
}
#Test
public void testMe() throws Exception {
when(MDC.get("correlation-id")).thenReturn("1234");
//Req param and header are intialized
restMvc.perform(get("/assignments").headers(headers).params(reqParams).principal(token).accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().is2xxSuccessful());
}
}
But i am getting error,
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.

To mock a static method you should use Mockito.mockStatic method like this:
try (var mockStatic = Mockito.mockStatic(MDC.class)) {
mockStatic.when(() -> MDC.get("correlation-id"))
.thenReturn("1234");
// rest of the test...
}
You can read more about it in the Mockito documentation. Additionally, I've reproduced the problem and tested the solution - you can see a commit in a GitHub repository with all required code. Please note that usage of mockito-inline is required for this to work - see this part of the docs.
In case of your test, it would probably better to go with the approach proposed by #knittl in the comment - instead of mocking the get method, a value can be set in the MDC using the put method: MDC.put("correlation-id", "1234"). I've included both approaches in the GitHub repo mentioned before - both tests pass.

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.

Unit Test Spring ApplicationEventPublisher

I am trying to write a unit test for the following code:
public void doSomething(List<Object> someObjects){
// Some logic I want to test...
eventPublisher.publishEvent(someEvent);
}
So in the project I call this method that perform some logic to the list of objects, and then as a result it publish the Application event with the result a method that was recieved.
I want to verify the logic inside that method with unit test but I am not sure how I can write the unit test if the method does not return nothing directly but publish a domain event. What is the correct way to do this?
thanks for your help.
As part of unit test, it is enough to verify if the `eventPublisher. is called with the correct argument. And also, remember that unit test is the document for the each every line of code for the method to be tested.
You have to mock the eventPublisher in test and use ArgumentCaptor to capture the argument which is being passed while invoking the message.
Ideally, your test should look like:
public class ClassToBeTestedTest {
#Mock
private ApplicationEventPublisher eventPublisher;
#InjectMocks
private ClassToBeTested classToBeTested;
#Captor
private ArgumentCaptor<SomeClass> captor;
#Before
public void init() {
initMocks(this);
}
#Test
public void testSend() throws Exception {
classToBeTested.doSomething(Arrays.asList());
verify(eventPublisher).publishEvent(captor.capture());
assertThat(captor.getValue(), is("expected value"))
}
}

Multiple runwith for a junit test class

Does any one know how to tackle this.
#RunWith(SpringJUnit4ClassRunner.class)
#RunWith(Parametrized.class)
#ContextConfiguration("/META-INF/blah-spring-test.xml")
public class BlahTest
..
so i want to have a spring nature test and at the same time want to have it parameterized to avoid code duplication ...
You can't use two runners as it noted in the commented post. You should use the Parameterized runner as use Spring's TestContextManager to load the Spring context.
#Before
public void before() throws Exception {
new TestContextManager(getClass()).prepareTestInstance(this);
}
As of Spring Framework 4.2, JUnit-based integration tests can now be executed with JUnit rules instead of the SpringJUnit4ClassRunner. This allows Spring-based integration tests to be run with alternative runners like JUnit’s Parameterized or third-party runners such as the MockitoJUnitRunner. See more details on spring doc.
Building upon John B's answer using TestContextManager, one can also call beforeTestMethod() and afterTestMethod() on it to better simulate SpringJUnit4ClassRunner's behaviour (for instance loading database with #Sql).
These methods require a Method parameter, so one can for instance take advantage of JUnit4's TestName rule to get the current test method's name and then retrieving it by reflection.
private static TestContextManager springTestContext
= new TestContextManager(BlahTest.class);
#Rule
public TestName testName = new TestName();
#Before
public void before() throws Exception {
springTestContext.prepareTestInstance(this);
springTestContext.beforeTestMethod(this,
getClass().getMethod(testName.getMethodName()));
}
#After
public void after() throws Exception {
springTestContext.afterTestMethod(this,
getClass().getMethod(testName.getMethodName()), null);
}

Unit testing with Mockito

I am writing unit tests for service layer in my spring application.
Here is my service class
#Service
public class StubRequestService implements RequestService {
#Autowired
private RequestDao requestDao;
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
#Override
public Request getRequest(Long RequestId) {
Request dataRequest = requestDao.find(requestId);
return dataRequest;
}
}
Here is my test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {
#Mock
public RequestDao requestDao;
StubRequestService stubRequestService; // How can we Autowire this ?
#org.junit.Before
public void init() {
stubRequestService = new StubRequestService(); // to avoid this
stubRequestService.setRequestDao(dataRequestDao);
// Is it necessary to explicitly set all autowired elements ?
// If I comment/remove above setter then I get nullPointerException
}
#Test
public void testGetRequest() {
Request request = new Request();
request.setPatientCnt("3");
when(requestDao.find(anyLong())).thenReturn(request);
assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
}
}
Its working fine but I have few questions
How can we Autowire service class in test ? I am using constructor in init() method to create service object.
Do we have to set all Autowire element for service class ? For ex StubRequestService have autowired RequestDao which I need to set explicitly before calling test method otherwise it giveds nullPointerException as requestDao is null in StubRequestService.getRequest method.
Which are the good practices to follow while unit testing Spring service layer ? (If I am doing anything wrong).
Your test is fine. It doesn't even have to have the #ContextConfiguration annotation.
The whole point of dependency injection frameworks like Spring is to be able to unit test services by simply instantiating them, setting mock dependencies, and then call their methods.
You're doing it correctly. You don't need to have a Spring context for such unit tests. That's why they're called unit tests: they test it in isolation of all their actual dependencies, Spring included.
Side note: assuming you're using JUnit, the arguments of the assertXxx method should be swapped. The expected value comes before the actual value. It becomes important when the assertion fails and you have a message like "expecting 6 but was 3" rather than "expecting 3 but was 6".
If you really feel that it will make your tests easier to understand - you can initialize a spring context and fetch all of the objects from there. However, usually it will require creating a separate spring configuration XML file specifically for tests therefore I would not recommend it.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
(and 3) Basically, I prefer testing each component of my application in total isolation from eachother and that's why I do not recommend what I described in [1].
What that means, is you take a separate logical slice of your application and test only it, while fully mocking up everything it tries to access.
Let's say you have three classes:
//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
public void setWebservice(Webservice ws) {...};
public MyObject getMyObject() {...};
}
//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
public void setOutputStream(OutputStream os) {...};
public void viewMyObject(MyObject mo) {...};
}
//Main entry point of our MyObject fetch-process-display workflow
class Controller {
public void setDataAccessLayer(DataAccessLayer dal) {...};
public void setViewLayer(ViewLayer vl) {...};
public void showMyObject() {
MyObject mo = dal.getMyObject();
...some processing here maybe...
vl.viewMyObject(mo);
}
}
Now, what tests can we write here?
Test if DataAccessLayer properly converts the object from mocked up WS to our domain object.
Test if ViewLayer properly formats the object given to him and writes it to mocked up output stream.
Test if Controller takes an object from mocked up DataAccessLayer processes it properly and sends it to mocked up ViewLayer.
Or You can use springockito
https://bitbucket.org/kubek2k/springockito/wiki/Home, it will make your tests cleaner

How to use session in unit test?

I have a spring service method that gets an object stored in the session (using FacesContext) as follows:
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
and I would like to put that object in session in unit test before invoking the method.
so i tried the solution in this post:
Spring Test session scope bean using Junit
and in my test method i put the object in session before calling the service, but the service throws an exception when trying to get the object from the session, i guess that this is because the facescontext is not available, what do you think ?
I am using Spring, Junit, JSF2, please advise, thanks.
With Spring 3.2 this is very easier
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(...)
#WebAppConfiguration
public class SessionTest {
#Autowired
MockHttpSession session;
#Test
public void sessionAttributeTest() throws Exception {
MyObject myObject = session.getAttribute("myObject");
...
}
}
More information: Request and Session Scoped Beans
I'm assuming that you're talking about HttpSession.
Create a mock session, tell the mock session to always return this object when its getAttribute method is called with the name used by the object under test, and pass this mock session rather than a real one to the object under test.
Mocking APIs such as Mockito or EasyMock will help doing that.
EDIT: Suppose the method you want to test looks like this:
public String foo() {
// some lines of code
MyObject o =
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
// some more lines of code, using o.
}
You could refactor it like this:
public String foo() {
// some lines of code
MyObject o = getMyObjectFromSession();
// some more lines of code, using o.
}
protected MyObject getMyObjectFromSession() {
return (MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
}
And you could then use a mocking framework to do something like this (pseudo-code):
// mockFoobar is the object to test. We just mock its getMyObjectFromSession method
FooBar mockFoobar = mock(Foobar.class);
MyObject objectInSession = new MyObject();
when(mockFoobar.getMyObjectFromSession()).thenReturn(objectInSession);
String s = mockFoobar.foo();
assertEquals("expected result", s);
Add spring-mock to your test classpath which gives you org.springframework.mock.web.MockHttpSession. This is a pretty simple implementation that you can create with new without a Java EE container.
The JAR also contains mocks for requests, responses and everything else.
Another solution is to use MockRunner which does the same.

Resources