Unit Test Spring ApplicationEventPublisher - spring

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"))
}
}

Related

How do I write a junit test for a simple controller method that returns a service method call?

I am new to Spring, and also Junit testing. I would like to test this method so it increases my test line coverage.
Here is the class:
#RestController
#RequestMapping("/")
public class SummaryController {
private final SummaryService summaryService;
#Autowired
public SummaryController(SummaryService summaryService) {
this.summaryService = summaryService;
}
#GetMapping("/summary")
#ResponseBody
public List<SummaryObject> getDirectoryList() {
return summaryService.getSummary();
}
}
So this is a springboot/react web. application. When we click the "summary" page, we load a table with my local file directory content. summaryService.getSummary() is the method that returns a populated object model which my controller then sends the same object back to the front-end.
Here is a test I wrote to try to test this method.
#WebMvcTest (TestController.class)
public class Test Controller {
List<SummaryObject> summaryObject;
#Mock
private SummaryService summaryService;
#Test
void testControllerMatch() {
this.summaryObject = new ArrayList<>();
this.summaryObject.add(new SummaryObjectModel("file name", "file size", "last modified"));
when(SummaryService.getSummary()).thenReturn(summaryObjectModel);
assertEquals(SummaryObjectModel, SummaryService.getSummary());
}
}
The test is passing, but I do not think it is testing what I want. I wanted it to test that the return statement returns the List of SummaryObjects.
I know the return statement is not tested because when I see coverage, the "return summaryService.getSummary();" line in my controller doesn't get tested (its red, not green. thus not tested).
How can I test that method indeed returns a List of SummaryObjects ?
What other kinds of tests can I write for such a simple method? How do I make that line covered in coverage ("green")?
Your controller code is not being covered because your test doesn't call that endpoint. To do that you might use mockMvc which apparently you were going to use. I suggest that you go through the example in the official doc.
Conceptually, your test will look like this:
#WebMvcTest
public class ControllerTest {
#MockBean
private SummaryService summaryService;
#Test
void testControllerMatch() {
List<SummaryObject> summaries = new SummaryObject("file name", "file size", "last modified");
when(summaryService.getSummary()).thenReturn(summaryObjectModel);
// the /summary endpoint gets called here and the controller code will finally be called
mockMvc.perform(get("/summary"))
.andExpect(status().isOk())
.andExpect(content().json("<JSON representation of the summary list >"));
}
}

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.

Сamunda replace behaviour for external tasks in tests

I created simple Camunda spring boot project and also created simple BPMN process with switcher. (5.5 KB)
I used service task with external implementation as a spring beans. I want to write tests for process but I don't want to test how beans works. Because in general I use external implementation for connection to DB and save parameter to context or REST call to internal apps. For example I want skip execute service task(one) and instead set variables for switcher. I tried to use camunda-bpm-assert-scenario for test process and wrote simple test WorkflowTest.
I noticed if I use #MockBean for One.class then Camunda skip delegate execution. If use #Mock then Camunda execute delegate execution.
PS Sorry for bad english
One
#Service
public class One implements JavaDelegate {
private final Random random = new Random();
#Override
public void execute(DelegateExecution execution) throws Exception {
System.out.println("Hello, One!");
execution.setVariable("check", isValue());
}
public boolean isValue() {
return random.nextBoolean();
}
}
WorkflowTest
#SpringBootTest
#RunWith(SpringRunner.class)
#Deployment(resources = "process.bpmn")
public class WorkflowTest extends AbstractProcessEngineRuleTest {
#Mock
private ProcessScenario insuranceApplication;
#MockBean
private One one;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
Mocks.register("one", one);
}
#Test
public void shouldExecuteHappyPath() throws Exception {
// given
when(insuranceApplication.waitsAtServiceTask("Task_generator")).thenReturn(externalTaskDelegate -> {
externalTaskDelegate.complete(withVariables("check", true));
}
);
String processDefinitionKey = "camunda-test-process";
Scenario scenario = Scenario.run(insuranceApplication)
.startByKey(processDefinitionKey) // either just start process by key ...
.execute();
verify(insuranceApplication).hasFinished("end_true");
verify(insuranceApplication, never()).hasStarted("three");
verify(insuranceApplication, atLeastOnce()).hasStarted("two");
assertThat(scenario.instance(insuranceApplication)).variables().containsEntry("check", true);
}
}
I found two solutions:
It's a little hack. If user #MockBean for delegate in a test. The delegate will be skipped but you have trouble with process engine variables.
Create two beans with one qualifier and use profiles for testing and production. I used to default profile for local start and test profile for testing.

How to unit test a service which sets attribute to newly created object

I want to write unit test for one of my service to verify certain fields get assigned.
public void createNewRecord(Dto dto) {
Record record = new Record();
record.setName(dto.getName());
record.setDetail(dto.getDetail());
repo.save(record);
}
I don't have a constructor for dto because a record has many attributes and some of them get assigned from other methods. My previous plan is: mock the record and verify setName() and setDetail methods are called once. But there is no way to inject mocked record into this service. Do I have to change my previous code? Any thought is appreciated.
There are several approaches:
First:
change method to this
public void createNewRecord(Record record, Dao dao)
Second:
Use PowerMockito to mock constructor
Third:
Use factory or com.google.inject.Provider for construct record (I prefer this)
Forth:
If record constructor is simple and setters for record also don't have some special logic then you can mock only repo and verify repo's argument.
Mocks should be used for mocking a dependency of the tested object, not for mocking an inner object in the tested method.
Why don't you mock the repo instance and then you would verify with you mock api that repo.save() is called with the expected record ?
It seems to be a straight way to unit test your method.
You can use Mockito and its #Captor annotation to capture the arguments passed to the repo instance. You can #Mock the Dto object to create expectations for the getName() and getDetail() methods and assert that the results of invoking the setName() and setDetail() methods on the captured Record instance are the same values expected from the Dto object. For example:
#Mock
private Repo repo;
#Mock
private Dto dto;
#Captor
private ArgumentCaptor<Record> recordArgumentCaptor;
private Service service;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.service = new Service(repo);
}
#Test
public void shouldCreateNewRecord() {
when(dto.getName()).thenReturn("NAME");
when(dto.getDetail()).thenReturn("DETAIL");
service.createNewRecord(dto);
verify(repo).save(recordArgumentCaptor.capture());
Record record = recordArgumentCaptor.getValue();
assertThat(record.getName(), is(equalTo(dto.getName())));
assertThat(record.getDetail(), is(equalTo(dto.getDetail())));
}

Is it possible to verify arbitrary interaction using Mockito in a compact way?

It is easy to verify that specific interaction (specific method call) occurred on a mock object in Mockito, and there is verifyZeroInteractions() for checking that no interactions occurred at all. Suppose I'm testing an interface such as that of a logger, with methods such as info(), warn(), error() etc. In a specific scenario I know one of these methods should be called, but I don't really care which one. Is there a compact way to check that any interaction with the mock object occurred without the need of specifying which exactly method should be called? Or perhaps such a mechanism is not necessary because "the Mockito way" of testing this would be different from what I imagine?
With log4j, to test the logger I do the following setup:
#Mock private Appender log4jAppender;
private Logger logger;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
Logger root = Logger.getRootLogger();
if (!root.getAllAppenders().hasMoreElements()) {
// No appenders means log4j is not initialized!
BasicConfigurator.configure();
}
logger = Logger.getLogger(MyClassWhichLogs.class);
logger.addAppender(log4jAppender);
}
and then in my test I do the following:
verifyZeroInteractions(log4jAppender);
or
verify(log4jAppender).doAppend(any(LoggingEvent.class);
If you need to test the values logged, you can provide a captor instead:
ArgumentCaptor<LoggingEvent> logCaptor = ArgumentCaptor.forClass(LoggingEvent.class);
verify(log4jAppender).doAppend(logCaptor.capture());
assertTrue(logCaptor.getValue().contains("my text to match");
While this doesn't necessarily answer the generalized question (I don't think what you're looking for exists), it may solve this specific problem for testing logging.
If you want to check that any interaction occurred with a mock object, you can use the Mockito.mockingDetails() method and check that the number of invocations is not zero. Of course you could also do more detailed assertions based on the mocking details, but I guess just checking for not zero answers your question.
#ExtendWith(MockitoExtension.class)
public class TestClass {
#Mock
private Logger logger;
#InjectMocks
private Service service;
#Test
public void testMethod_shouldLogMultipleTimes() {
service.testMethod();
assertThat(Mockito.mockingDetails(logger).getInvocations().size()).isNotZero();
}
}
The code example uses assertj to check that the number of invocations is not zero.
If you can externalise the creation of your logger object from the class under test, there's no reason why you can't write your own test implementation of the Log Interface that will record which methods were exercised and inject it as part of your test setup.
Mock libraries do a lot of good, but sometimes there are corner cases like you have found where they may not cover your needs.
If you write your own implementation for testing like this, and inject it into yourt test class under test, then you can assert on getCount() > 0
public class LoggerTestSupportImpl implements ILogger {
private int count = 0;
#Override
public int getCount() {
return count;
}
#Override
public void info(String message) {
count++;
}
#Override
public void warn(String message) {
count++;
}
}

Resources