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
Related
I have a test class that is annotated with #Spy and #InjectMocks and tested using Mockito. The class under test has a value (url) that is retrieved from the application.properties file. I'd like to test whether this url is being set correctly within the method that uses it. I can do this if I remove the #Spy and #InjectMocks annotations and use #Autowire and #SpringBootTest. However, that breaks other tests that use the spy functionality, so I'm just wondering if there's any way we can keep the spy working and test all our methods inside the same file, maybe without the need to bring in the #SpringBootTest annotation and the autowiring? The workaround we're using for now is to have two files, one that uses the spy and the other that tests the specific method to do with the properties file and that requires the full context to load, but not sure that's the best solution here?
Here is the test with the spy:
#ExtendWith(MockitoExtension.class)
class ProviderHelperServiceTest {
#Spy
#InjectMocks
ProviderHelperService providerHelperService;
#Value("${viaduct-url}")
String viaductUrl;
#Test
void testGetRequestBodyUriSpec() {
WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken");
final String[] url = new String[1];
requestBodyUriSpec.attributes(httpHeaders -> {
url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate");
});
// Fails as url[0] comes back as null. Disabled and moved to another file.
assertEquals(viaductUrl, url[0]);
}
#SpringBootTest
class ProviderHelperService2Test {
#Autowired
ProviderHelperService providerHelperService;
#Value("${viaduct-url}")
String viaductUrl;
#Test
void testGetRequestBodyUriSpec() {
WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken");
final String[] url = new String[1];
requestBodyUriSpec.attributes(httpHeaders -> {
url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate");
});
assertEquals(viaductUrl, url[0]);
}
}
And here is the method under test:
public class ProviderHelperService {
#Value("${viaduct-url}")
String viaductUrl;
public WebClient.RequestBodyUriSpec getRequestBodyUriSpec(String sifToken) {
WebClient.RequestBodyUriSpec requestBodyUriSpec = WebClient.create().post();
requestBodyUriSpec.header("Authorization", sifToken);
requestBodyUriSpec.uri(viaductUrl);
return requestBodyUriSpec;
}
}
The cleanest way to perform such tests is to replace field injection with constructor injection, and then you can quite easily confirm that the value that's passed into the class comes back out the service call.
If you're using Boot, it's usually best to replace use of #Value with #ConfigurationProperties. Depending on the specifics, you can either pass the whole properties object to the service's constructor or write an #Bean configuration method that unpacks the relevant properties and passes them as plain constructor parameters with new.
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.
As the title states, I can call verifyPrivate but it always gives me success even if I pass the wrong parameters to it.
Real example
#RunWith(PowerMockRunner.class)
#PrepareForTest({MyService.class})
public class MyServiceTest {
#InjectMocks
MyService service;
#Mock
OperationSivRepo operationSivRepo;
#Test
public void getNbInscriptions_should_call_getNbOperationsSiv_with_OPERATION_INSCRIPTION_GAGE() throws Exception {
// GIVEN
Role role = Role.ADMINISTRATEUR;
String operator = "operator";
SivDto sivDto = new SivDto();
// WHEN
service.getNbInscriptions(operator, role, sivDto);
// THEN
verifyPrivate(service).invoke("privateMethod", operator, Role.ADMINISTRATEUR, sivDto);
}
}
Now this code will succeed, even if I do something like
// THEN
verifyPrivate(service).invoke("privateMethod", "other string", Role.USER, new SivDto());
Maybe I'm missing something but I just can't figure it out.
Firstly. Did you put a debug point in privateMethod and see how many times it is getting called? This would have given you some hint.
It is getting called two times. Once when you call
service.getNbInscriptions(operator, role, sivDto);
and once when you use
verifyPrivate(service).invoke("privateMethod", operator, Role.ADMINISTRATEUR, sivDto);
Second time since it is getting called with the arguments you passed to invoke method, the tests always succeed.
Use Spy instead of Mock
Instead of
#InjectMocks
MyService service;
Use
#Spy
MyService myservice = new MyService(operationSivRepo)
Wiht this, second invocation to the method is not made and arguments are verified properly.
I am trying to mock a call in my test but I am getting a error as its calling the real method than mocking it.
This is my method
#Value("${omega.aws.nonprod-profile}")
private String nonProdProfile;
#Autowired
AwsService awsService;
public List<SecurityGroup> getAllSecurityGroups() {
AmazonEC2 ec2 = configSetter();
return awsService.getAllSecurityGroups(ec2);
}
protected AmazonEC2 configSetter() {
ProfileCredentialsProvider credentials = new ProfileCredentialsProvider(nonProdProfile);
ClientConfiguration clientCfg = new ClientConfiguration();
clientCfg.setProxyHost(this.proxyHost);
clientCfg.setProxyPort(Integer.valueOf(proxyPort));
clientCfg.setProtocol(Protocol.HTTPS);
return new AmazonEC2Client(credentials, clientCfg);
}
Here is my test class
#InjectMocks
private AwsConfigurationLocal subject;
#Mock
private AwsService awsService;
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
securityGroup = new SecurityGroup();
List<SecurityGroup> result = Collections.singletonList(securityGroup);
Mockito.when(awsService.getAllSecurityGroups(ec2)).thenReturn(result);
List<SecurityGroup> actual = subject.getAllSecurityGroups();
assertThat(actual, CoreMatchers.is(equals(result)));
}
The test actually calls the protected method configSetter and fails when setting a proxy. Help me understand what I am doing wrong here.
subject.getAllSecurityGroups(); calls real configSetter() which returns real AmazonEC2 which in turn is passed on to awsService.getAllSecurityGroups(ec2);. The parameter doesn't match your mock ec2 so the default mock implementation is returned (I guess it's null) as a actual.
So the issue is: there is nothing that would prevent real implementation of configSetter() to be called.
If you annotate subject with #Spy and do
Mockito.when(subject.configSetter()).then(ec2);
it should work as expected.
That being said there's a lot of set up done only to check simple invocation being delegated. This is due intermixing of two responsibility in AwsConfigurationLocal - creation of AmazonEC2Client and providing getAllSecurityGroups(). If you move the former into separate class (let's say AmazonEC2ClientFactor) everything should fall in place.
Try using powerMockito to return the mocked instance of AmazonEC2
#RunWith(PowerMockRunner.class)
#PrepareForTest({AwsConfigurationLocal.class})//assuming this is your test class
#InjectMocks
private AwsConfigurationLocal subject=PowerMockito.spy(AwsConfigurationLocal.class);//or try Mockito.spy instead, whichever works
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
PowerMockito.doReturn(ec2).when(subject).configSetter().withNoArguments();
//your code
}
I am having some troubles passing a dependency while unit testing with JUnit.
Consider these pieces of code:
This is the dependacy injecton into the class which i want to test, lets call it Controller.
#Inject private FastPowering fastPowering;
And this is the unit test:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
#Mock
FastPowering fastPower;
#InjectMocks
Controller controller;
#Test
public void test() {
assertEquals(
(controller.computeAnswer(new BigDecimal(2), 2)).longValue(),
(long) Math.pow(2, 2));
}
}
It seems that fastPower is null, please explain how to fix that.
Null pointer exception , because of calling the #injected field (fastPower) inside the .computeAnswer method)
Edit:
Solved i should have read about the difference between #Mock and #Spy...
Due to a lot of comments I am adding some more context to the solution
The difference is that in mock, you are creating a complete mock or fake object while in spy, there is the real object and you just spying or stubbing specific methods of it. While in spy objects, of course, since it is a real method, when you are not stubbing the method, then it will call the real method behavior.
If fastPower is annotated as #Mock it's methods are dummy, yet controller.computeAnswer depends on them to compute. One must provide behaviour.
If spy is used without stubbing then the real implementation of fastPower is being executed which eventually returns desired value.
Another option is to use a real FastPowering instance
https://github.com/mockito/mockito/wiki/Using-Spies-(and-Fakes)
https://github.com/mockito/mockito/wiki/Mocking-Object-Creation
And some stackoverflow thread outlining the difference Mocking vs. Spying in mocking frameworks
Short Answer: Replace #Mock with #Spy and should be working fine
Use MockitoAnnotations.initMocks to initiate the #Mock and #InjectMocks objects. Your test would look something like:
#Mock
FastPowering fastPower;
#InjectMocks
Controller controller;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
....
}
After debugging I found a reason. This is because of the org.powermock.core.MockRepository#instanceMocks collection. It doesn't contain a mock for a field with #InjectMocks annotation (Controller controller in your case).
To solve it try to use the #Spy annotation in the field declaration with initializing of them and #PrepareForTest above the class declaration:
#PrepareForTest(Controller.class)
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
#Mock
FastPowering fastPower;
#Spy
#InjectMocks
Controller controller = new Controller();
#Test
public void test() {
//...
}
}
In my case it helped. Using of the Mockitoannotations.initMocks(this) method is not required, it doesn't affect the result.
I fixed this by removing the extraneous new instance I was creating in my #Before method (see example below). It was also fixed by moving MockitoAnnotations.initMocks(this) after initializing myClass, but since Mockito created myClass anyway, that solution was inferior.
// Note - you may need #mock(name="foo") to help mockito differentiate props
// if they have the same type
#mock
private Thing something;
#InjectMocks
private MyClass myClass;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this); // Moving this below the next line fixed it...
myClass = new MyClass() // But just remove this line and let Mockito do the work
}
Useful to note that the use of MockitoAnnotations.initMocks(this); needs to come at the end of your #Before/setUp() method.
If it is at the top of your setUp() then it may cause other mocked classes to not be initialised.
I ran into this error myself just now and placing .initMocks at the end of my #Before solved my issue.
I was using the wrong #Test annotations, If you want to use #InjectMocks and #Mock in your Mockito Test, then you should
add #ExtendWith(MockitoExtension.class) annotation on your test class
annotate your test methods with #Test (org.junit.jupiter.api.Test) and not the #Test (org.junit.Test) annotation. be careful with the import that you are using for this annotation.
This works on mockito-core:3.6.28
2 more things to check:
1. Mocking the behaviours of fastPower. What should this mocked object return, when it methods are called? i.e. when(fastPower.doSomething()).thenReturn(some_kind_of_object);
2. Check if the controller.computeAnswer() does not return NULL for the input of new BigDecimal(2), 2)).longValue(), (long) Math.pow(2, 2).