Junit test case in spring boot - java

How can i write junit test for void method?
i have following method in service layer
#Override
public void add(Demo demo) throws ApiError {
if (!repository.existsByNameAndAge(demo.getName(), demo.getAge())) {
throw new ApiError(HttpStatus.BAD_REQUEST, "bad request");
}
Integer count = newRepository.countByName(cart.getName());
newRepository.save(new Demo(demo.getName(), demo.getAge(), demo.getCity(), count));
}
here is my service method and i want to do junit test case for it. but it's return type is void. i want to do testing of each statment. how can i done junit testing of this please suggest me..

Sorry I wrote the answer for Junit5 and then noticed you tagged Junit4, I will post it anyway, the idea is the same and differences in the code should be minor. What you can do is using Mockito to inject mocks and verify that the methods are called with the parameters you expect them be called. I would write 2 test cases: one to check the exception is thrown and the repositories are not called and another one to check the repository is properly saving:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#Mock
private Repo repository;
#Mock
private NewRepo newRepository;
#Captor
private ArgumentCaptor<Demo> demoCaptor;
#InjectMocks
private MyService service;
#Test
void throwsIfDoesNotExistForGivenNameAndAge() {
when(repository.existsByNameAndAge("name", 12)).thenReturn(false);
assertThrows(ApiError.class, () -> service.add(new Demo("name", 12, "city", 10)));
verify(newRepository, times(0)).countByName(anyString());
verify(newRepository, times(0)).save(any(Demo.class));
}
#Test
void savesToNewRepositoryWithRightValues() {
when(repository.existsByNameAndAge("name", 12)).thenReturn(true);
when(newRepository.countByName("cart")).thenReturn(10);
service.add(new Demo("name", 12, "city", 10));
verify(newRepository, times(1)).save(demoCaptor.capture());
final Demo actual = captor.getValue();
final Demo expected = //create your expected here
assertEquals(expected, actual);
}
Remember to implement equals() and hashCode() in your Demo class, or another option could be asserting on the fields of Demo you care about. I'm also not sure what cart on which you are calling getName() is, but if it's another dependency of your service you will have to inject it as a mock and properly set it up with when() and return value.
The differences in terms of junit4/5 should be (not 100% sure it's all of them, going with my memory here):
the imports
the #ExtendWith should be #RunWith(mockitojunitrunner.class)
the test for the exception should be #Test(expected = ApiError.class) instead of using assertThrows

This function basically saves the data if the data is not available in the repository, Junits are meant to check if this function is working as expected. Here you will test for 2 cases
when data is available in the repository: For this mock repository.existsByNameAndAge(...) and return false
,in test case use expected #Test(expected=ApiError.class)
when it is not: In this case use opposite of the above case and don't use the expected attribute.

Related

Mockito expecting argument mismatch

I'm writing a Unit test with JUnit 4 and Mockito 4.6.1. I'm mocking an interface method with a specific input value. When a different value is passed, I'm expecting an argument mismatch error, but it's not being thrown.
Consider the following example:
public class SomeTest {
#Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, Mockito.withSettings().strictness(Strictness.STRICT_STUBS));
// Only mock test(true) and not test(false).
Mockito.when(mock.test(true)).thenReturn(1);
Assert.assertEquals(mock.test(true), 1);
Assert.assertEquals(
// Expecting argument mismatch error
mock.test(false),
2
);
}
interface SomeInterface {
int test(Boolean arg);
}
}
I'm mocking SomeInterface.test(true) to return 1. This works as expected. Now when I call mock.test(false), I'm expecting an argument mismatch because it's not defined in the mock setup and strict mode is enabled. Instead, it returns 0 as if it was mocked.
Am I missing something that's causing this to happen?
Problem 1. We must enable STRICT_STUBS for test.
STRICT_STUBS - ensures clean tests, reduces test code duplication,
improves debuggability. Best combination of flexibility and
productivity. Highly recommended. Planned as default for Mockito v4.
Enable it via MockitoRule, MockitoJUnitRunner or MockitoSession. See
STRICT_STUBS for the details.
According to documentation it can be done via MockitoJUnitRunner.StrictStubs.class
#RunWith(MockitoJUnitRunner.StrictStubs.class)
Or strictness rule
#Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
Problem 2. Argument strictness does not work when mock definition and mock invocation perform in one source class.
According to Mockito source code:
If stubbing and invocation are in the same source file we assume they are in
the test code,
and we don't flag it as mismatch:
Summarise. The working test will be:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
import static org.mockito.Mockito.*;
#RunWith(MockitoJUnitRunner.StrictStubs.class)
public class SomeTest {
#Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, withSettings().strictness(Strictness.STRICT_STUBS));
Mockito.when(mock.test(true)).thenReturn(1);
SomeInterfaceImpl someInterface = new SomeInterfaceImpl(mock);
Assert.assertEquals(someInterface.test(), 2);
}
}
public interface SomeInterface {
int test(Boolean arg);
}
public class SomeInterfaceImpl {
private SomeInterface someInterface;
public SomeInterfaceImpl(SomeInterface someInterface) {
this.someInterface = someInterface;
}
public int test() {
return someInterface.test(false);
}
}
On execution we get:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'test' method:
someInterface.test(false);
-> at com.example.SomeInterfaceImpl.test(SomeInterfaceImpl.java:10)
- has following stubbing(s) with different arguments:
1. someInterface.test(true);
-> at com.example.SomeTest.test(SomeTest.java:26)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
Please use 'will().given()' or 'doReturn().when()' API for stubbing.
- stubbed method is intentionally invoked with different arguments by code under test
Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
For more information see javadoc for PotentialStubbingProblem class.
More preferable solution
You can define the default answer with IllegalArgumentException for your mock in case if mock is executed with mismatch arguments. This solution will work always without source file restrictions.
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.doReturn;
#RunWith(MockitoJUnitRunner.class)
public class SomeTest {
#Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, Mockito.withSettings().defaultAnswer(
invocation -> {
throw new IllegalArgumentException(String.format("You cannot invoke %s with %s", invocation.getMethod(), java.util.Arrays.toString(invocation.getArguments())));
}
));
// Only mock test(true) and not test(false).
doReturn(1).when(mock).test(true);
Assert.assertEquals(mock.test(true), 1);
Assert.assertEquals(
// Expecting argument mismatch error
mock.test(false),
2
);
}
}
On execution we get:
java.lang.IllegalArgumentException: You cannot invoke public abstract int com.example.SomeInterface.test(java.lang.Boolean) with [false]

Mocking object instantiation with new in Java using PowerMock not working

I am trying to Unit Test a class in Java.
Code for this class: ToBeTested
public class ToBeTested {
private Collaborator collaborator;
public ToBeTested() {
System.out.println("ToBeTested: Constructor");
}
public void start() {
System.out.println("ToBeTested: Start");
collaborator = new Collaborator();
}
}
This class ToBeTested depends on another class, Collaborator.
Code for class: Collaborator
public class Collaborator {
Collaborator() {
System.out.println("Collaborator: Constructor");
}
}
While testing the class ToBeTested, I want to stub instantiation of Collaborator. That's a dependency I want to mock and I don't want it's constructor to be called.
I'm using Junit (v4.12) and PowerMock (v1.6.1).
Code for Test Class: TestToBeTested
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.powermock.api.easymock.PowerMock.*;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ToBeTested.class, Collaborator.class})
public class TestToBeTested {
#Mock
private Collaborator collaborator;
private ToBeTested toBeTested;
#Before
public void setUp() throws Exception {
collaborator = createMock(Collaborator.class);
expectNew(collaborator.getClass()).andReturn(null);
toBeTested = new ToBeTested();
}
#Test
public void test() {
replayAll();
toBeTested.start();
verifyAll();
}
}
My understanding is that this will mock or stub out Collaborator and it's constructor should not be called. However, when I run the test, I notice that original constructor of Collaborator is called.
Output of test run:
ToBeTested: Constructor
ToBeTested: Start
Collaborator: Constructor
I'm very new to Java and Unit Testing in Java, so I apologize if I'm doing a very fundamental mistake here.
During my quest to find out the root cause, I have referred to following SO questions:
PowerMock's expectNew() isn't mocking a constructor as expected
PowerMock expectNew how to specify the type of the parameters
Not able to mock constructor using PowerMock
https://dzone.com/articles/using-powermock-mock
Thank you very much in advance for help/suggestions/feedback.
One possible reason that it might not be working could be this line:
expectNew(collaborator.getClass()).andReturn(null);
collaborator is a mocked instance which means it's "getClass()" method is going to return Collaborator$CGLIBMockedWithPowermock or something like that -- not the Collaborator class you want it to be. So you might get it to work simply by changing that line to:
expectNew(Collaborator.class).andReturn(null);

Mocking CGLIB enhanced objects

Is it true that mockito can't mock objects that were already enhanced by CGLIB?
public class Article {
#Autowired
private dbRequestHandler
#Autowired
private filesystemRequestHandler
#Transactional
public ArticleDTO getArticleContents() {
//extractText() and then save the data in DTO
//extractImages() and then save the data in DTO
// some other calls to other databases to save data in dto
return articleDTO;
}
public void extractText() {
//call to DB
}
public void extractImages() {
// call to file system
}
}
public class IntegrationTest {
#Autowired
private Article article;
//setup method {
articleMock = Mockito.spy(article);
doNothing().when(articleMock).extractImages();
}
}
In the above example when it comes to doNothing().when(articleMock).extractImages(); it actually calls the real function. On a closer look articleMock gets enhanced two times. One cause of autowiring and second time cause of spying.
If I can't spy on enhaced objects, then how can I test the getArticle() method in my Integration test, so that I can verify a proper DTO is returned.
Note : I actually don't want to test the method which does filesystem calls. just the DB ones. thats why I need to test the getArticle method.
If I understand correctly your class is wired by Spring. Spring uses CGLIB to ensure transactional behaviour only if there is no interface, which is implemented by your object. If there is an interface, it uses simple JDK Dynamic Proxies. (see http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html)
Maybe you could try to extract an interface, and let Spring to use dynamic proxies. Maybe then Mockito could perform better.
If you run as a true unit test and not as an integration test, you need not run in a container having Spring autowire for you. In one of your comments, I think you alluded to trying this, and you noted that there was an endless set of chained object references which you would have to provide as well. But there is a way around that. Mockito provides some predefined Answer classes that you can initialize your mock with. You may want to look at RETURNS_DEEP_STUBS, which will possibly get you around this problem.
Will you please update your question with ready-to-go compilable code. Here's some code review suggestions:
Issues with this question code:
Article.java missing import: org.springframework.beans.factory.annotation.Autowired
Article.java missing import: org.springframework.transaction.annotation.Transactional
Article.java attribute syntax issue: dbRequestHandler
Article.java attribute syntax issue: filesystemRequestHandler
Article.java method has no initialized return statement: articleDTO
Here's what you maybe should use as you questionCode with the above issues fixed:
Article.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
public class Article {
#Autowired
private Object dbRequestHandler;
#Autowired
private Object filesystemRequestHandler;
#Transactional
public ArticleDTO getArticleContents() {
// extractText() and then save the data in DTO
// extractImages() and then save the data in DTO
// some other calls to other databases to save data in dto
ArticleDTO articleDTO = null;
return articleDTO;
}
public void extractText() {
// call to DB
}
public void extractImages() {
// call to file system
}
}
IntegrationTest.java is a poor name for a testClass because it's to generic. I would suggest ArticleTest for a java unit test.
ArticleTest.java
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.beans.factory.annotation.Autowired;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassWithPrivate.class)
public class ArticleTest {
#InjectMocks
private Article cut;
#Mock
private Object dbRequestHandler;
#Mock
private Object filesystemRequestHandler;
#Test
public void testeExtractImages() {
/* Initialization */
Article articleMock = Mockito.spy(cut);
/* Mock Setup */
Mockito.doNothing().when(articleMock).extractImages();
/* Test Method */
ArticleDTO result = cut.getArticleContents();
/* Asserts */
Assert.assertNull(result);
}
}
You can utilize AdditionalAnswers.delegatesTo method. In following example, the secondProxyDoingMocking declaration creates something like a spy (compare with implementation of spy() method) except it uses "lightweight" method delegation.
import org.mockito.AdditionalAnswers;
public class ArticleTest {
#Autowired
private Article firstProxyDoingAutowiring;
#Test
public void testExtractImages() {
Article secondProxyDoingMocking = Mockito.mock(Article.class,
Mockito.withSettings().defaultAnswer(
AdditionalAnswers.delegatesTo(firstProxyDoingAutowiring)
)
);
Mockito.doNothing().when(secondProxyDoingMocking).extractImages();
...
}
}
I didn't test this example, however I assembled it from my working code. My use case was similar: return constant value for given method, call real method for all remaining methods of Spring #Transactional-annotated bean.

mockito, spy- not sure how it's done for partial mocking

I have a class, where i want to mock certain methods of the class and test the others. That is the only way i can verity and assert that it's working.
class UnderTest{
public void methodToTest(){
methodToCall1()
methodToCall2()
}
public void methodToCall1(){
}
public void methodToCall2(){
}
}
Now, since i want to test the first method, i want to create a partial mock of UnderTest so i can verify that those two methods were called.
How do i achieve this in Mockito?
Thanks for your help!
You mentioned you wanted to do two things:
1. Create real partial mocks
2. Verify method invocations
However, since your goal is to validate that methodToCall1() and methodToCall2() were actually invoked, all you need to do is spy on the real object. This can be accomplished with the following code block:
//Spy UnderTest and call methodToTest()
UnderTest mUnderTest = new UnderTest();
UnderTest spyUnderTest = Spy(mUnderTest);
spyUnderTest.methodToTest();
//Verify methodToCall1() and methodToCall2() were invoked
verify(spyUnderTest).methodToCall1();
verify(spyUnderTest).methodToCall2();
If one of the methods are not called, for example methodToCall1, an Exception will be thrown:
Exception in thread "main" Wanted but not invoked:
undertest.methodToCall1();
...
package foo;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class FooTest {
#Spy
private UnderTest underTest;
#Test
public void whenMethodToTestExecutedThenMethods1And2AreCalled() {
// Act
underTest.methodToTest();
// Assert
verify(underTest).methodToCall1();
verify(underTest).methodToCall2();
}
}

passing Parameterized input using Mockitos

I am using Mockito for unit testing. I am wondering if its possible to send Parametrized input parameters with as in Junit testing
e.g
#InjectMocks
MockClass mockClass = new MockClass();
#Test
public void mockTestMethod()
{
mockClass.testMethod(stringInput);
// here I want to pass a list of String inputs
// this is possible in Junit through Parameterized.class..
// wondering if its can be done in Mockito
}
In JUnit, Parameterized tests use a special runner that ensure that the test is instantiated multiple times, so each test method is called multiple times. Mockito is a tool for writing specific unit tests, so there is no built-in ability to run the same test multiple times with different Mockito expectations.
If you want your test conditions to change, your best bet is to do one of the following:
Parameterize your test using JUnit, with a parameter for the mock inputs you want;
Run a loop of different parameters in your test, which unfortunately avoids the "test one thing per method" philosophy
Extract a method that actually performs the test, and create a new #Test method for each mock you want.
Note that there's no prohibition on using mock objects as #Parameterized test parameters. If you're looking to parameterize based on mocks, you can do that, possibly creating the mock and setting the expectations in a static method on the test.
Note about runners: This Parameterized test runner conflicts with Mockito's MockitoJUnitRunner: Each test class can only have one runner. You'll want to switch to #Before and #After methods or a Mockito JUnit4 rule for your setup, if you use them both.
As an example, compressed from a different answer that explains more about Parameterized runners versus JUnit rules and lifting from the JUnit4 Parameterized Test doc page and MockitoRule doc page:
#RunWith(Parameterized.class)
public class YourComponentTest {
#Rule public MockitoRule rule = MockitoJUnit.rule();
#Mock YourDep mockYourDep;
#Parameters public static Collection<Object[]> data() { /* Return the values */ }
public YourComponentTest(Parameter parameter) { /* Save the parameter to a field */ }
#Test public void test() { /* Use the field value in assertions */ }
}
If you are stuck with an older version of mockito where MockitoRule isn't available, the other possibility is to initialize the mocks explicitely with MockitoAnnotations.initMocks:
#RunWith(Parameterized.class)
public class YourComponentTest {
#Mock YourDep mockYourDep;
#Parameter
public Parameter parameter;
#Parameters public static Collection<Object[]> data() { /* Return the values */ }
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test public void test() { /* Use the field value in assertions */ }
}
You can use the JUnitParamsRunner. Here's how I do it:
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Arrays;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
#RunWith(value = JUnitParamsRunner.class)
public class ParameterizedMockitoTest {
#InjectMocks
private SomeService someService;
#Mock
private SomeOtherService someOtherService;
#Before
public void setup() {
initMocks(this);
}
#Test
#Parameters(method = "getParameters")
public void testWithParameters(Boolean parameter, Boolean expected) throws Exception {
when(someOtherService.getSomething()).thenReturn(new Something());
Boolean testObject = someService.getTestObject(parameter);
assertThat(testObject, is(expected));
}
#Test
public void testSomeBasicStuffWithoutParameters() {
int i = 0;
assertThat(i, is(0));
}
public Iterable getParameters() {
return Arrays.asList(new Object[][]{
{Boolean.TRUE, Boolean.TRUE},
{Boolean.FALSE, Boolean.FALSE},
});
}
}
What solved it for me was:
Class level annotation of #ExtendWith(MockitoExtension.class)
Annotate each mock object with #Mock
#InjectMocks on the test class. Or a setup method annotated with #BeforeEach where you initialise the class to be tested.
if you need the #test annotation, make sure you import org.junit.jupiter.api.Test. org.junit.test will not work!
I'm using mockito version 4.

Categories