I can't find a way to test my update method using JUnit. I am mocking the necessary repo and services. But when I call findById(Mockito.any()) it can't find anything so it throws an exception.
Here is my test class:
#ExtendWith(MockitoExtension.class)
class ApplicantServiceTest {
#Mock
private ApplicantRepository applicantRepository;
#Mock
private CreditRatingService creditRatingService;
#Mock
private ApplicantMapper applicantMapper;
#InjectMocks
private ApplicantService applicantService;
#Test
void update() {
Applicant applicant = getSampleApplicants().get(1);
Applicant updatedApplicant = new Applicant(1L,2141245L,"ege","c0skun",2523,"21412412",null,null);
Mockito.when(applicantService.update(applicantMapper.toDTO(applicant),Mockito.any())).thenReturn(applicant);
Assert.assertEquals(applicant.getIdentificationNumber(),updatedApplicant.getIdentificationNumber());
Assert.assertEquals(applicant.getPhoneNumber(), updatedApplicant.getPhoneNumber());
Assert.assertEquals(applicant.getFirstName(), updatedApplicant.getFirstName());
assertEquals(applicant.getMonthlyIncome(), updatedApplicant.getMonthlyIncome());
Assert.assertEquals(applicant.getId(), updatedApplicant.getId());
}
And this is the method I am trying to test:
public Applicant update(ApplicantDTO applicantDTO,Long id) {
Applicant byId = getById(id);
if (applicantDTO.getMonthlyIncome()!=byId.getMonthlyIncome()){
byId.setMonthlyIncome(applicantDTO.getMonthlyIncome());
}
if (!Objects.equals(applicantDTO.getFirstName(), byId.getFirstName())){
byId.setFirstName(applicantDTO.getFirstName());
}
if (!Objects.equals(applicantDTO.getLastName(), byId.getLastName())){
byId.setLastName(applicantDTO.getLastName());
}
if (!Objects.equals(applicantDTO.getPhoneNumber(), byId.getPhoneNumber())){
byId.setPhoneNumber(applicantDTO.getPhoneNumber());
}
if (!Objects.equals(applicantDTO.getIdentificationNumber(), byId.getIdentificationNumber())){
byId.setIdentificationNumber(applicantDTO.getIdentificationNumber());
}
return applicantRepository.save(byId);
}
This is the output:
17:43:19.520 [main] ERROR com.egecoskun.finalproject.services.ApplicantService - Applicant not found by id : null
Related Applicant not found with : [id : null]
EntityNotFoundException(details=Some Special Details)
at com.egecoskun.finalproject.services.ApplicantService.lambda$getById$0(ApplicantService.java:47)
at java.base/java.util.Optional.orElseThrow(Optional.java:403)
at com.egecoskun.finalproject.services.ApplicantService.getById(ApplicantService.java:45)
at com.egecoskun.finalproject.services.ApplicantService.update(ApplicantService.java:75)
at com.egecoskun.finalproject.services.ApplicantServiceTest.update(ApplicantServiceTest.java:136)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
Can someone explain how to unit test my update method?
The problem with your test code is that you've mixed mocking a method for a mock object (e.g. applicantRepository) with calling the tested method on an object under test (applicantService). We use when(...) to define the mocked behavior in Mockito and after that the actually tested code should be called (without the when()).
What you probably want to do is: mock retrieving the applicant object by ID, mock saving the modified object, run the update method to test it and assert the result.
To mock the behavior of the applicantRepository you need to tell Mockito what is the expected outcome of its methods calls, for example (I made a few assumptions for the code that is not attached):
when(applicantRepository.findById(anyLong())).thenReturn(applicantFromDb);
when(applicantRepository.save(notNull())).thenAnswer(returnsFirstArg());
(applicantFromDb is an object created in the test method)
After that when you call your update method, the mocked behavior above will be used:
Applicant result = applicantService.update(...);
Now you can assert the result returned from the tested method.
ApplicationService is your class under test. The exception you're getting is coming from ApplicationService.getById() method called at the start of update() method you're testing. You did not show us that method, but at a guess it calls the repository to get an instance by id. Your Repository mock is not set up to return anything when asked for any entity, so it returns null. Which seems to trigger the orElseThrow clause in the getById() method.
As such what you're seeing is likely the expected behaviour - when trying to update with an id that does not exist in the repository (repository returns null) service throws an exception. If you want to test the case where the entity is present you need to mock the repository appropriately.
Related
I am getting following exception while running the tests. I am using Mockito for mocking. The hints mentioned by Mockito library are not helping.
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
..........
Test Code from DomainTestFactory. When I run the following test, I see the exception.
#Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}
private List<SomeModel> getSomeList() {
SomeModel model = Mockito.mock(SomeModel.class);
Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
Mockito.when(model.getAddress()).thenReturn("Address");
return Arrays.asList(model);
}
public class SomeModel extends SomeInputModel{
protected String address;
protected List<SomeClass> properties;
public SomeModel() {
this.Properties = new java.util.ArrayList<SomeClass>();
}
public String getAddress() {
return this.address;
}
}
public class SomeInputModel{
public NetworkInputModel() {
this.Properties = new java.util.ArrayList<SomeClass>();
}
protected String Name;
protected List<SomeClass> properties;
public String getName() {
return this.Name;
}
public void setName(String value) {
this.Name = value;
}
}
You're nesting mocking inside of mocking. You're calling getSomeList(), which does some mocking, before you've finished the mocking for MyMainModel. Mockito doesn't like it when you do this.
Replace
#Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}
with
#Test
public myTest(){
MyMainModel mainModel = Mockito.mock(MyMainModel.class);
List<SomeModel> someModelList = getSomeList();
Mockito.when(mainModel.getList()).thenReturn(someModelList);
}
To understand why this causes a problem, you need to know a little about how Mockito works, and also be aware in what order expressions and statements are evaluated in Java.
Mockito can't read your source code, so in order to figure out what you are asking it to do, it relies a lot on static state. When you call a method on a mock object, Mockito records the details of the call in an internal list of invocations. The when method reads the last of these invocations off the list and records this invocation in the OngoingStubbing object it returns.
The line
Mockito.when(mainModel.getList()).thenReturn(someModelList);
causes the following interactions with Mockito:
Mock method mainModel.getList() is called,
Static method when is called,
Method thenReturn is called on the OngoingStubbing object returned by the when method.
The thenReturn method can then instruct the mock it received via the OngoingStubbing method to handle any suitable call to the getList method to return someModelList.
In fact, as Mockito can't see your code, you can also write your mocking as follows:
mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);
This style is somewhat less clear to read, especially since in this case the null has to be casted, but it generates the same sequence of interactions with Mockito and will achieve the same result as the line above.
However, the line
Mockito.when(mainModel.getList()).thenReturn(getSomeList());
causes the following interactions with Mockito:
Mock method mainModel.getList() is called,
Static method when is called,
A new mock of SomeModel is created (inside getSomeList()),
Mock method model.getName() is called,
At this point Mockito gets confused. It thought you were mocking mainModel.getList(), but now you're telling it you want to mock the model.getName() method. To Mockito, it looks like you're doing the following:
when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);
This looks silly to Mockito as it can't be sure what you're doing with mainModel.getList().
Note that we did not get to the thenReturn method call, as the JVM needs to evaluate the parameters to this method before it can call the method. In this case, this means calling the getSomeList() method.
Generally it is a bad design decision to rely on static state, as Mockito does, because it can lead to cases where the Principle of Least Astonishment is violated. However, Mockito's design does make for clear and expressive mocking, even if it leads to astonishment sometimes.
Finally, recent versions of Mockito add an extra line to the error message above. This extra line indicates you may be in the same situation as this question:
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
For those who use com.nhaarman.mockitokotlin2.mock {}
Workaround 1
This error occurs when, for example, we create a mock inside another mock
mock {
on { x() } doReturn mock {
on { y() } doReturn z()
}
}
The solution to this is to create the child mock in a variable and use the variable in the scope of the parent mock to prevent the mock creation from being explicitly nested.
val liveDataMock = mock {
on { y() } doReturn z()
}
mock {
on { x() } doReturn liveDataMock
}
Workaround 2
Make sure all your mocks that should have a thenReturn.
GL
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.
For mocking of void methods try out below:
//Kotlin Syntax
Mockito.`when`(voidMethodCall())
.then {
Unit //Do Nothing
}
AbcService abcService = mock(AbcService.class);
Check the syntax:
doThrow(new RunTimeException()).when(abcService).add(any(), any())
Common Mistake as seen below:
A. doThrow(new RunTimeException()).when(abcService.add(any(), any()))
Similarly, check for when().thenReturn(), so on.
I am so exited with detailed answer of #Luke Woodward that want to share a workaround.
As #Luke Woodward explained, we can not have two calls like
when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);
Than can occurs in call chain.
But in case you will use construction:
doReturn(mockToken("token3")).when(mock).getAccessToken();
when
OAuth2AccessToken mockToken(String tokenVal){
OAuth2AccessToken token = Mockito.mock(OAuth2AccessToken.class);
doReturn( 60 ).when(token).getExpiresIn();
doReturn(tokenVal).when(token).getValue();
return token;
}
all will works as expected.
I want to write tests for service, but it looks like JPARepository.save method returns null.
Here is my code from ServiceTest
#ExtendWith(MockitoExtension.class)
class PatientServiceTest {
#Mock
private PatientRepository patientRepository;
#Spy
private PatientMapper mapper = new PatientMapper();
#InjectMocks
private PatientService serviceUnderTests;
#Test
void whenCreatePatientByService_thenPatientRepositoryIsNotEmpty() {
PatientModel patientModel = new PatientModel(134, "Pawel", "Nowak",
"pawel#test.pl", "123456789");
serviceUnderTests.create(patientModel);
assertNotNull(patientRepository.findAll());
}
Service code
public PatientModel create(PatientModel patientModel) {
PatientEntity patientEntity = mapper.from(patientModel);
PatientEntity savedPatient = repository.save(patientEntity);
return mapper.from(savedPatient);
}
Here is a screenshot from debugging mode:
Repository is just interface extends JPARepository, and mapper has only two methods which map Entity to Model and Model to Entity.
IntelliJ says:
java.lang.NullPointerException: Cannot invoke "com.nowakpawel.healthcenter.repository.entity.PatientEntity.getId()" because "entity" is null
at com.nowakpawel.healthcenter.service.mapper.PatientMapper.from(PatientMapper.java:24)
at com.nowakpawel.healthcenter.service.PatientService.create(PatientService.java:29)
at com.nowakpawel.healthcenter.service.PatientServiceTest.whenCreatePatientByService_thenPatientRepositoryIsNotEmpty(PatientServiceTest.java:44)
Why when I want to map Entity to Model in Service, Java put null into from method, while PatientEntity isn't null?
PatientModel is passed as parameter to the method and it is converted into PatientEntity patientEntity object. As its a unit test and the PatientRepository has been mocked, so when something is mocked you need to return the value of the mock.
So value should be returned for the statement
PatientEntity savedPatient = repository.save(patientEntity);
You can write the test case in this way
void whenCreatePatientByService_thenPatientRepositoryIsNotEmpty() {
PatientModel patientModel = new PatientModel(134, "Pawel", "Nowak",
"pawel#test.pl", "123456789");
PatientEntity patientEntity = new PatientEntity();
patientEntity.setXxxx("Nowak"); // Other values as per getter and setters
doReturn(patientEntity).when(patientRepository).save(Matchers.any());
PatientModel response = serviceUnderTests.create(patientModel);
assertNotNull(response);
asserEquals("Nowak", response.getXxxxx()); //Getter method name here
}
Thank you guys for answering my question. I have missed that part of Mockito ;)
But I still have trouble. Here is my code now:
#Test
void whenCreatePatientByService_thenPatientRepositoryIsNotEmpty() {
PatientModel patientModel = new PatientModel(14, "Pawel", "Nowak", "pawel#test.pl",
"123456789");
PatientEntity entity = mapper.from(patientModel);
when(patientRepository.save(entity)).thenReturn(entity);
serviceUnderTests.create(patientModel);
assertNotNull(patientRepository.findAll());
}
and got an error:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'save' method:
patientRepository.save(
PatientEntity [id=14, firstName='Pawel', lastName='Nowak', emailAddress='pawel#test.pl', phoneNumber='123456789']
);
-> at com.nowakpawel.healthcenter.service.PatientService.create(PatientService.java:28)
- has following stubbing(s) with different arguments:
1. patientRepository.save(
PatientEntity [id=14, firstName='Pawel', lastName='Nowak', emailAddress='pawel#test.pl', phoneNumber='123456789']
);
-> at com.nowakpawel.healthcenter.service.PatientServiceTest.whenCreatePatientByService_thenPatientRepositoryIsNotEmpty(PatientServiceTest.java:48)
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.
As far as I understand - the problem is
stubbed method is intentionally invoked with different arguments by code under test
Am I right? And why this is an error, while I passed Entity to repository save method?
Here is my whole project in case it's needed -> https://github.com/nowakpawel/health-center
Ok, I found a solution. Just added equals and hashCode methods to patientEntity.
Please close this thread as it is solved.
I have a something like this one:
class User {
String name;
}
class Contacts {
User getUser() {
return new User();
}
}
I do it this way so that in my test I can mock the method like:
#ExtendWith(MockitoExtension.class)
class ContactsTest {
#Spy
private Contacts sut;
#Mock
private User user;
#Test
void testSomething() {
doReturn(user).when(sut).getUser();
}
#Test
void testGetUser() {
// verify(new User(), times(1));
// verify(user, times(1));
}
}
How could I test testGetUser?
The only two ideas I had which are commented above gave me these errors:
For the first one:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type User and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();
For the second one
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
First of all, since your unit under test is the Contacts class there is no need to spy on it and mock its behavior as this is the class you need to test. So, I would go ahead and remove that.
Now regarding your question, what you need to test is the actual outcome of the getUser, so the simplest of all tests would be to invoke the method on an instance of Contacts and assert that the returned result is a non-null instance of a User object.
If you really want to test that the constructor of the User class gets called, you will need to actually mock that call using either PowerMock (which is advise against and most likely does not even work with JUnit5) or use mockito-inline.
Once this is done you would be able to return a mocked User instance from the constructor call which you can then run assertions on.
I have a problem with getting result from method which is not mocked. I dont want to mock the result but to achieve the real result from this method. The method works in application, so this is not the problem. I have a test:
#Test
public void shouldGetCompaniesToSelect() throws Exception {
Company company = new Company("company", new Address());
Company relatedCompany1 = new Company("relatedCompanyName1", new Address());
Company notRelatedCompany = new Company("notRelatedCompanyName", new Address());
Company relatedCompany2 = new Company("relatedCompanyName2", new Address());
CompanyRelation companyRelation1 = new CompanyRelation(relatedCompany1);
CompanyRelation companyRelation2 = new CompanyRelation(relatedCompany2);
company.getCompanyRelations().add(companyRelation1);
company.getCompanyRelations().add(companyRelation2);
when(companyServiceMock.findAll()).thenReturn(Arrays.asList
(company, relatedCompany1, notRelatedCompany, relatedCompany2));
when(companyServiceMock.findOne(1L)).thenReturn(company);
List<Company> companiesToSelect = companyServiceMock.findCompaniesToSelect(company);
mockMvc.perform(get("/company/1"))
.andExpect(model().attribute("companiesToSelect", hasSize(1)))
.andExpect(model().attribute("companiesToSelect", hasItem(
hasProperty("relatedCompany", hasProperty(
"name", is("notRelatedCompanyName")
)))));
}
There are 2 mocked methods (findAll and findOne) and then I want to execute method and get real results from findCompaniesToSelect(company - this is object created to test). Size of the companiesToSelect should be 1, but it returns 0.
findCompaniesToSelect method:
public List<Company> findCompaniesToSelect(Company company) {
List<Company> companiesToSelect = companyRepository.findAll();
for (CompanyRelation companyRelation :
company.getCompanyRelations()) {
companiesToSelect.remove(companyRelation.getRelatedCompany());
}
companiesToSelect.remove(company);
return companiesToSelect;
}
How can I do that?
EDIT 1:
Okay, so I've changed it into #Spy and changed stubs into:
Mockito.doReturn(Arrays.asList(company, relatedCompany1,
notRelatedCompany, relatedCompany2)).when(companyServiceMock).findAll();
Mockito.doReturn(company).when(companyServiceMock).findOne(1L);
But when I run the test, the findCompaniesToSelect() method is using real companies instead of mocked companies.
Okay, so now the problem is when the findCompaniesToSelect() method is called, the companyRepository.findAll method is called. I want to call mocked findAll method from test to get mocked companies instead of real companies.
EDIT 2:
Okay, the problem was because findCompaniesToSelect() method uses repository, not a service. :D
If you want to have a combination of mocked methods and real method calls on an object then you need to use a spy instead of a mock.
#Spy
CompanyService companyServiceSpy
You will need to stub your mock methods differently though. using doReturn(...).when(...) instead of the stubbing you are currently using.
Have a look at http://www.baeldung.com/mockito-spy for more information on using spies.
EDIT: Since you are mocking some behaviour in your test you should use a standalone set up for your MockMvc object and inject the mock into your controller like so:
MockMvc mockMvc;
#Spy
CompanyService companyServiceSpy;
#InjectMocks
CompanyController companyController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(companyController).build();
}
EDIT 2: You may also be able to resolve this just by changing #Spy to #SpyBean in your current set up. Not 100% sure on this because i'm not fully familiar with how Spring boot sets up tests.
This is happening because your service class i.e. companyService is mocked.
I should suggested to use Restassured for testing your Restservice, where it didn't require to create mock for different methods of your RestService components i.e. you don't need to mock CompanyService.
For further reading:
RestAssured
Example Code for RestAssured testing
I have to write the following unit test cases in testng:
saveProductTest which would return productId if product details are saved successfully in DB.
modifyProductTest, it should use previously saved productId as a parameter.
I am taking the product details input(PrdouctName, ReleaseDate) for saveProductTest and modifyProductTest method from an XML file using testNg data providers.Since productId is generated in save method, I have to pass it to the modify method.
What is the best way to pass output of one test method to another method in testng.
With all due respect to simendsjo, the fact that all tests should be independent from each other is a dogmatic approach that has a lot of exceptions.
Back to the original question: 1) use dependent methods and 2) store the intermediate result in a field (TestNG doesn't recreate your instances from scratch, so that field will retain its value).
For example
private int mResult;
#Test
public void f1() {
mResult = ...
}
#Test(dependsOnMethods = "f1")
public void f2() {
// use mResult
}
With the ITestContext object. It's a object available globally at the Suite context and disponible via parameter in each #Test.
For example:
#Test
public void test1(ITestContext context, Method method) throws Exception {
// ...
context.setAttribute(Constantes.LISTA_PEDIDOS, listPaisPedidos);
// ...
}
#Test
public void test2(ITestContext context, Method method) throws Exception {
List<PaisPedido> listPaisPedido = (List<PaisPedido>)
context.getAttribute(Constantes.LISTA_PEDIDOS);
// ...
}
Each unit test should be independent of other tests so you more easily can see what fails. You can have a helper method saving the product and returning the id and call this from both tests.