Im trying to make an unit test for this method
public void loadProductsFromAPI() {
List<ProductEntity> databaseProducts = productService.getAllProductsFromBd();
CountryTenant.country.forEach((key, value) -> {
List<ProductEntity> productsByCountry = databaseProducts.stream()
.filter(product -> key.toString().equalsIgnoreCase(product.getCountry()))
.collect(Collectors.toList());
List<ProductDTO> productsFromAPI = productsApi.getAllProductsFromAPI(value);
List<ProductEntity> processedProducts = productService.persistProductsFromApi(key, productsByCountry, productsFromAPI);
});
}
My problem its the List<ProcuctEntity> productsByCountry, that variable is created in the execution time. How can I mock that in my test case?
With mockito I already mock my products from db.
Mockito.when(productService.getAllProductsFromBd()).thenReturn(databaseProducts);
If you ever find yourself trying to mock a value, you probably need to clean up your test plan. In this case, the productsByCountry is a value that is calculated inside your method: The correctness of that stream pipeline is part of what you need to verify. Mocking it would simply bypass ensuring that your logic is correct.
Instead, return appropriate test data from the mock productService and ensure that the correct filtered result if passed to persistProducts.
Related
I have this Method that throws an IllegalArgumentException when somebody tries to call it with value 0.
I want to write several stub and mock tests - for example - for the method getFrequentRenterPoints.
I coudn't figure out any "when" or "verify" statements which are used in mocks so I mixed parts of mocks and parts of stubs together and came up with this:
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
//given
Movie movieMock = mock(Movie.class);
//when
movieMock.getFrequentRenterPoints(0);
//then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}
Is it okay to have in a class with other Mocks, or if I want to use assertThrows should I change this into a stub? Or can I use assertThrows with mocks?
The answer from Benjamin Eckardt is correct.
But I try to approach this question from another point of view: when to use mocking? This is one of my favourite answers to that question.
So in practise:
Say your code is like (just guessing all the business objects & names...):
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// this is just the rest of code in which your test does not enter because
// of thrown exception
return somethingToReturn();
}
For this you do not need and you should not want to mock anything here.
But when things get more complicated like your method would be like:
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// What is this?
// It is injected in the Movie - say - like
//
// #Resource
// private RenterPointService renterPointService;
List<RenterPoints> unfiltered = renterPointService.getRenterPoints(renterId);
return filterToFrequent(unfiltered);
}
Now if you test renterId >= 1 what about this renterPointService how do you instantiate it to not get NPE? Say if it is injected and requires to pull up heavy framework for testing or it requires very heavy construction or so? You do not, you mock it.
You are testing the class Movie not the class RenterPointService so you should not bother to think how RenterPointService works but what it returns when used in the class Movie. Still: you do not mock the class Movie which you are testing.
Assuming using you are using Mockito and using annotations the mocking would be then done in your test class like:
#Mock
private RenterPointService renterPointService;
#InjectMocks
private Movie movie;
Then you would do mocking of methods for renterPointService like:
when(renterPointService.getRenterPoints(anyInt))
.thenReturn(someListContaineingMockRenterPointsForThisTest);
Usually you expect the tested production method to throw and not the mock or stub. I drafted it by using new Movie().
Furthermore in that case it does not really make sense to separate the calls into when and then because if movieMock.getFrequentRenterPoints(0); throws, assertThrows(...) will never be executed.
To apply the given/when/then structure with the assertThrows API you could extract the passed lambda in some way, but I personally don't see much benefit in it.
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
// given
Movie movieMock = new Movie();
// when/then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}
I have a simple Unit Test that is failing. Hopefully I can explain this in simple terms as I've been looking at it for hours and I see what the issue is, but I am not too familiar the underlying theory behind Mocks so I am a bit confused and cannot fix it. I will summarize the issue very quickly and then paste the code below.
Basically, in my test method called getAllValidModelsTest(), it uses a for loop to iterate thru enum values of object type DeviceModel. There are only 5: [EX3400_24P, EX4300_32F, EX4300_48MP, SRX_345, FAUX].
So inside the for loop, before the Assert statement (Junit), it makes a static method call to getDevice(deviceId) and it should from there return a Device object. The first line under the for loop in the getAllValidModelsTest() mocks the elementMock object to return the current model that is being iterated over in the DeviceModels[] array that was returned from the .values() call on the enums DeviceModel class.
So my issue is, when it jumps in the 2nd iteration in my for loop (counting from 1), the Assert fails , because the 0th element in the DeviceModel[] array is obviously EX4300_32F, but in the #Before setUp annotation it is being mocked to return EX3400_24P. But the weird thing is, under the for loop inside the getAllValidModelsTest() method, it is being overridden/mocked again to return to the current model that is being iterated through when .getModel is called on the elementMock object, so it should be returning the SAME value...
This is how the class SwitchDeviceFactoryTest.java is constructed (the class with the Unit Test):
#PowerMockIgnore({"javax.net.ssl.*"})
#RunWith(PowerMockRunner.class)
#PrepareForTest({DataGatewayFactory.class, SwitchConfig.class, RouterConfig.class})
public class SwitchDeviceFactoryTest {
String deviceId = "testdevice";
String ip = "1.1.1.1";
DataGateway dbMock = Mockito.mock(DataGateway.class);
SwitchConfig swConfigMock = PowerMockito.mock(SwitchConfig.class);
RouterConfig routerConfigMock = PowerMockito.mock(RouterConfig.class);
TransportDeviceSecretsInfo secrets = new TransportDeviceSecretsInfo();
TransportDeviceSecretsData secretsData = new TransportDeviceSecretsData("root","rootPw", "sshUser", "sshPass", "snmpAuthPass", "snmpPrivPass");
IElement elementMock = Mockito.mock(IElement.class);
ITransportDeviceSecretsCrud transportDeviceSecretsCrud = mock(ITransportDeviceSecretsCrud.class);
ISwitchConfigCrud switchConfigCrud = mock(ISwitchConfigCrud.class);
IRouterConfigCrud routerConfigCrud = mock(IRouterConfigCrud.class);
IElementCrud elementCrud = mock(IElementCrud.class);
This is my setUp method that runs before the test. The only variables that should be of importance are the elementMock object, specifically the one being mocked to return the EX3400_24P object:
#Before
public void setup() throws Exception {
secrets.setSecretsData(secretsData);
PowerMockito.mockStatic(DataGatewayFactory.class);
Mockito.when(DataGatewayFactory.getInstance()).thenReturn(dbMock);
Mockito.when(dbMock.getTransportDeviceSecretsCrud()).thenReturn(transportDeviceSecretsCrud);
Mockito.when(transportDeviceSecretsCrud.getServerSecretsInfo(anyString())).thenReturn(Optional.of(secrets));
Mockito.when(transportDeviceSecretsCrud.getReportedSecretsInfo(anyString())).thenReturn(Optional.of(secrets));
when(dbMock.getElementCrud()).thenReturn(elementCrud);
doReturn(Optional.of(elementMock)).when(elementCrud).getById(anyString());
Mockito.when(elementMock.getModel()).thenReturn(DeviceModel.EX3400_24P.getModel());
Mockito.when(elementMock.getType()).thenReturn(ElementType.SWITCH);
Mockito.when(dbMock.getSwitchConfigCrud()).thenReturn(switchConfigCrud);
Mockito.when(switchConfigCrud.get(anyString())).thenReturn(Optional.of(swConfigMock));
Mockito.when(swConfigMock.getIp()).thenReturn(ip);
Mockito.when(dbMock.getRouterConfigCrud()).thenReturn(routerConfigCrud);
Mockito.when(routerConfigCrud.get(anyString())).thenReturn(Optional.of(routerConfigMock));
Mockito.when(routerConfigMock.getIp()).thenReturn(ip);
And the test method:
#Test
public void getAllValidModelsTest() throws Exception {
for (DeviceModel model: DeviceModel.values()) {
when(elementMock.getModel()).thenReturn(model.getModel());
if (model == DeviceModel.SRX_345)
when(elementMock.getType()).thenReturn(ElementType.ROUTER);
else
when(elementMock.getType()).thenReturn(ElementType.SWITCH);
Device device = DeviceFactory.getDevice(deviceId);
assertEquals(model, device.getModel());
}
}
The thing that doesn't make sense, is I was refactoring code, and only changed 2 lines (the elementCrud and elementMock .doReturn and .when calls) and it works perfectly fine on the develop branch.
When I debug, I can see that on the 2nd iteration of the for loop, .getModel returns EX3400_24P object inside the static getDevice method, when it should be returning model.getModel() , which would be the 2nd object being iterated on in the .values() enum array of DeviceModels... so it should be EX4300_32F.
On the develop branch, this works perfectly.... It's as if the Mockito mock object forgets what it's suppose to do when it jumps inside the DeviceFactory class inside the getDevice method once its called in my getAllValidModelsTest() method (i.e. Device device = DeviceFactory.getDevice(deviceId);)
Here is the .getDevice method from the DeviceFactory class:
public static Device getDevice(String serialNumber) throws Exception {
IElement element = dataGateway.getElementCrud().getById(serialNumber).get();
DeviceModel model = DeviceModel.valueOfLabel(element.getModel()); // right here is where it returns the wrong model... it returns EX3400_24P on the 2nd iteration
log.info("Found device {} in database", serialNumber);
if (serialNumber.startsWith(FakeDevicePrefix.ATGTEST.toString()) || serialNumber.startsWith(FakeDevicePrefix.FAKE.toString())) {
log.info("Detected FAKE/ATG serial number. Using FAUX device.");
model = DeviceModel.FAUX;
}
switch (element.getType()) {
case SWITCH:
SwitchConfig config = dataGateway.getSwitchConfigCrud().get(serialNumber).get();
return getDevice(serialNumber, config.getIp(), model);
case ROUTER:
RouterConfig rconfig = dataGateway.getRouterConfigCrud().get(serialNumber).get();
return getDevice(serialNumber, rconfig.getIp(), DeviceModel.SRX_345);
case PTP:
default:
log.warn("Unsupported device type {}", element.getType().toString());
throw new Exception("Unsupported device type " + element.getType().toString());
}
}
I did indeed comment out/remove the piece of code that mocks it to return EX3400_24P in the setUp() method with #Before annotation , but the tests fails with a NULL POINTER EXCEPTION at this point.
How does the .getModel method know to return what I mocked it to return in the previous class (SwitchDeviceFactoryTest.java) before it jumps into the DeviceFactory.java class? How does it remember that if I'm not passing it in as a variable into the getDevice() method?
Do I need to use PowerMock or something because this is a static method? How does this change anything?
Please help!
I'm making a test for a service with a mock.
The problem is to create and inject instance directly from the class to test.
The source is shown below.
public OrderOutDTO createOrder(OrderSessionDTO orderSessionDTO) {
Order order = orderRepository.save(new Order(orderSessionDTO));
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
CreateOrderReqDTO createOrderReqDTO = mock(CreateOrderReqDTO.class);
createTrace(order, callServiceOrder.getData().getReceipt().getTransactionHash(), Trace.PUBLIC);
return new OrderOutDTO(order, null);
}
and test source is shown below.
#Test
public void createOrder() {
// given
CallService callService = mock(CallService.class);
CreateOrderResDataDTO createOrderResDataDTO = mock(CreateOrderResDataDTO.class);
// when
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
// then
assertThat(order, is(Matchers.notNullValue()));
assertThat(order.getOrder(), is(Matchers.notNullValue()));
assertThat(order.getOrder().getReceiver().getName(), is("test"));
}
I thought this test would finish well. But in the code below, it returned null and failed.
// callService.createOrder(new CreateOrderReqDTO(order)) return null
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
It doesn't seem to recognize it because the service injects a new instance. I want the mock data returned. What should I do?
In the following line you're mocking behavior on createOrderReqDTO as param:
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
whereas further, you're passing some other object:
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
This behavior is not recognized, you would have to pass the same thing you mocked before.
I found it myself!
I use argumentMatchers.
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
to
when(callService.createOrder(any())).thenReturn(createOrderResDTO);
thank you.
i am using eclemma and trying to increase my test coverage:
so far this is my code:
public RolesResponse findRolesByTenant(RolesRequest rolesRequest)
{
RolesResponse rolesResponse = new RolesResponse();
List<Role> roleList = null;
if (StringUtils.isNotBlank(rolesRequest.getTenantCode()))
{
roleList = roleFunctionService.getAllRolesAndFunctionsByTenant(rolesRequest.getTenantCode());
}
if (CollectionUtils.isNotEmpty(roleList))
{
rolesResponse.setRoles(roleList);
}
else
{
rolesResponse.setError(LayerContextHolder.getErrorObject());
}
return rolesResponse;
}
and here is my test:
#Test
public void findRolesByTenantTest()
{
RolesRequest rolesRequest = new RolesRequest();
rolesRequest.setTenantCode("test");
ErrorObject errorObject = new ErrorObject();
RolesResponse rolesResponse = rolesProcessService.findRolesByTenant(rolesRequest);
Assert.assertNull(rolesResponse.getError());
}
the only line eclemma is highlighting in red is this one:
rolesResponse.setError(LayerContextHolder.getErrorObject());
can someone help me in constructing the final test needed to cover this line
thanks
I'm really not a fan of your test anyway - what are you trying to prove by the error being null? That the list came back with something? Also, are you certain that your service will return the result you want in your test every single time?
Don't think of tests in terms of coverage; this will lead to brittle tests and tests that give a false sense of security. What you want to do is write tests that cover each condition that the code could encounter, and the line coverage can follow from that.
From your code, I see two cases.
roleFunctionService#getAllRolesByFunctionAndTenant can return a non-empty list.
It's implied that the resultant rolesResponse#roles contains whatever was in the list provided by the method, and this should be verified.
It's also implied that there is no error set on the object, so it should be null.
roleFunctionService#getAllRolesByFunctionAndTenant can return an empty list
Either the resultant rolesResponse#roles are empty or null; it'd be better if it were empty.
It's implied that there is an error on the object, which is specifically provided by LayerContextHolder.getErrorObject(). You should check to see that it's exactly that.
You'll get to the whole approach of writing this test through the use of a mocking framework.
I have a void method and I want to test it. How do I do that?
Here's the method:
public void updateCustomerTagCount() {
List<String> fileList = ImportTagJob.fetchData();
try {
for (String tag : fileList) {
Long tagNo = Long.parseLong(tag);
Customer customer = DatabaseInterface.getCustomer(tagNo);
customer.incrementNoOfTimesRecycled();
DatabaseInterface.UpdateCustomer(customer);
}
} catch(IllegalArgumentException ex) {
ex.printStackTrace();
}
}
when the method returns void, you can't test the method output. Instead, you must test what are the expected consequences of that method. For example:
public class Echo {
String x;
public static void main(String[] args){
testVoidMethod();
}
private static void testVoidMethod() {
Echo e = new Echo();
//x == null
e.voidMethod("xyz");
System.out.println("xyz".equals(e.x)); //true expected
}
private void voidMethod(String s) {
x = s;
}
}
It might not be always true, but basic concept of unit test is to check if function works as expected and properly handling errors when unexpected parameters/situation is given.
So basically unit test is against the functions that takes input parameters and return some output so we can write those unit test.
The code like yours, however, includes some other dependency (database call) and that's something you can't execute unless you write integration-test code or real database connection related one and actually that's not recommended for unit test.
So what you need to do might be introducing unit test framework, especially Mockto/Powermock or some other stuff that provides object mocking feature. With those test framework, you can simulate database operation or other function call that is going to be happening outside of your test unit code.
Also, about how do I test void function, there is nothing you can with Assert feature to compare output since it returns nothing as you mentioned.
But still, there is a way for unit test.
Just call updateCustomerTagCount() to make sure function works. Even with just calling the function, those unit test can raise your unit test coverage.
Of course for your case, you need to mock
ImportTagJob.fetchData();
and
DatabaseInterface.getCustomer(tagNo);
and have to.
Let mocked
ImportTagJob.fetchData();
throw empty list as well as non-empty list and check if your code works as you expected. Add exception handling if necessary. In your code, there are two condition depends on whether fieList are null or non-null, you need to test it.
Also, mock those objects and let them throw IllegalArgumentException where you expect it to be thrown, and write an unit test if the function throws a exception. In Junit, it should be like
#Test(expected = IllegalArgumentException.class)
public void updateCustomerTagCountTest(){
// mock the objects
xxxxx.updateCustomerTagCount();
}
That way, you can ensure that function will throw exception properly when it has to.