Im helper method use ehcache, to reduce queries to Db. Now want to implement JUnit+Mockito test to ensure that ehcache works properly. Have such variant of test:
#Autowired
private DBService service;
#Autowired
private DiscountHelper discountHelper;
#Autowired
private CacheManager cacheManager;
#Before
public void setUp() throws Exception {
assertNotNull(cacheManager);
}
#Test
public void testGetDiscountWithCache() throws RuntimeException,
InterruptedException {
String id1 = "id1";
String id2 = "id2";
String id3 = "id3";
List<String> discountsId = new ArrayList<String>();
discountsId.add(id1);
discountsId.add(id2);
discountsId.add(id3);
List<Map<String, Object>> attrList = new ArrayList<Map<String, Object>>();
attrList.add(new Discount().getAttributes());
attrList.add(new Discount().getAttributes());
attrList.add(new Discount().getAttributes());
Cache cache = cacheManager.getCache(DiscountHelper.CACHE_NAME);
assertNotNull(cache);
assertEquals(0, cache.getSize());
// First run with empty cache
when(service.getAllItems(eq(Discount.TABLE_NAME))).thenReturn(attrList);
List<Discount> actualResult = discountHelper
.getAllDiscountsUsingCache();
assertNotNull(actualResult);
assertEquals(attrList.size(), actualResult.size());
verify(service).getAllItems(eq(Discount.TABLE_NAME));
cache = cacheManager.getCache(DiscountHelper.CACHE_NAME);
// In cache should be 1 record
assertNotNull(cache);
assertEquals(1, cache.getSize());
}
And test method is:
#Cacheable(cacheName = CACHE_NAME, refreshInterval = 1000 * 900, decoratedCacheType = DecoratedCacheType.REFRESHING_SELF_POPULATING_CACHE)
public List<Discount> getAllDiscountsUsingCache() throws RuntimeException,
InterruptedException {
List<Map<String, Object>> result = dbService
.getAllItems(Discount.TABLE_NAME);
List<Discount> discountList = new ArrayList<Discount>();
for (Map<String, Object> entry : result) {
discountList.add(new Discount(entry));
}
return discountList;
}
And this perfectly works. In test i`m sure that after invocation of method I get something in cache. As you can see I also verify that was called method getAllItems in db service. That is good for first time invocation. Next I add second invocation of discountHelper.getAllDiscountsUsingCache() in the same test like this:
when(service.getAllItems(eq(Discount.TABLE_NAME))).thenReturn(attrList);
actualResult = discountHelper
.getAllDiscountsUsingCache();
assertNotNull(actualResult);
assertEquals(attrList.size(), actualResult.size());
verify(service, times(0)).getAllItems(eq(Discount.TABLE_NAME));
So I just want to check that on second invocation there will not be calls to DB service via method getAllItems by this verify: verify(service, times(0)).getAllItems(eq(Discount.TABLE_NAME)); and result will be obtain from cache.
But it doesn't work, I still get invocation of DB method. I find this tutorial http://blog.goyello.com/2010/07/29/quick-start-with-ehcache-annotations-for-spring/ and try variant with delegate object for ehcache testing, but it still invoke db methods on test. What`s wrong?
By the way in production ehcache works good as I see in logs of tomcat, so this is a problem of test. Any suggestions how to fix it?
In the second invocation you use the same mock object (service) created (as I assume from a question's tag) by Springockito during the Spring context initialization. The mock remembers getAllItems() call from the first invocation. You can:
reset the mock before the second invocation using reset(service),
or
check after the second invocation) if there is still one (not two) getAllItems() invocation.
Related
I am trying to test my method for getting back all entities that exist in my db. I am using JUnit and Mockito. I have no experience with testing so far and this is how far I've got:
This is my method from the agency service to get back all entities, using the findAll() function of JpaRepository:
public List<AgencyDto> getAll() {
return repo.findAll().stream().map(agency -> mapper.mapToDto(agency)).collect(Collectors.toList());
}
#ExtendWith(MockitoExtension.class)
public class AgencyServiceTest {
#Mock
private AgencyRepository agencyRepository;
#InjectMocks
private AgencyService agencyService;
#Test
void getAgencies() {
List<Agency> existingAgencies = new ArrayList<Agency>();
Agency agency1 = new Agency();
Agency agency2 = new Agency();
existingAgencies.add(agency1);
existingAgencies.add(agency2);
when(agencyRepository.findAll()).thenReturn(existingAgencies);
List<AgencyDto> result = agencyService.getAll();
assertEquals(existingAgencies, result);
}
}
When running the test, the value for expected seems ok, but the value for actual is an empty array:
Expected :[com.project.DTOs.AgencyDto#245a26e1, com.project.DTOs.AgencyDto#4d63b624, com.project.DTOs.AgencyDto#466cf502]
Actual :[]
Is this not the right way to test get() methods? Am I doing something wrong when setting the actual result?
if i understand your code coreectly
agencyRepository.findAll() return List<Agency>
agencyService.getAll() return List<AgencyDto>
Issue is here
List<Agency> existingAgencies = new ArrayList<Agency>();
when(agencyRepository.findAll()).thenReturn(existingAgencies);
your mock returns an empty list, you need to add items to list
eg:
List<Agency> existingAgencies = new ArrayList<Agency>();
existingAgencies.add(agencyObjectInDB1);
existingAgencies.add(agencyObjectInDB2);
existingAgencies.add(agencyObjectInDB3);
//Mock repo call
when(agencyRepository.findAll()).thenReturn(existingAgencies);
//I assume Here inside getAll() , agencyRepository.findAll() is invoked
//getAll() converts Agency to AgencyDto
List<AgencyDto> result = agencyService.getAll();
// assert against pre-pepared list
assertEquals(agencies, result);
I need to write a test case for following method with out hitting actual DB or configuring in memory database
public void deleteExistingRecord( List<String> extSrcIds) throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("extSourceIds", extSrcIds);
String query = "DELETE FROM REG.EXT_ACTIONED_RECORD T WHERE EXT_SRC_ID in :extSourceIds";
executeUpdateNativeQuery(query, params);
}
If you want to check if a method is called, you have to create a mock. See the Mockito website for more details: https://site.mockito.org/
I've to write test for a class that calls an API and then processes the response. The class has two public functions and a private function. The first public method fetches a list of IDs. The second public method is called in a loop for every ID to get details associated with an ID. The private method is called inside the second public method, since the calls to fetch details based on id are made asynchronously.
I'm new to JUnits and while I understand that I should not test the API calls, just my functions, I still don't understand what should the unit tests assert.
Below are my functions:
public List<Integer> fetchVehicleIds(String datasetId) throws ApiException {
VehiclesApi vehiclesApi = new VehiclesApi();
List<Integer> vehicleIds;
vehicleIds = vehiclesApi.vehiclesGetIds(datasetId).getVehicleIds();
return vehicleIds;
}
public List<VehicleResponse> fetchVehicleDetails(String datasetId, List<Integer> vehicleIds) throws InterruptedException, ApiException {
CountDownLatch latch = new CountDownLatch(vehicleIds.size());
List<VehicleResponse> vehiclesList = new ArrayList<>();
for (Integer vehicleId: vehicleIds) {
populateEachVehicleDetail(datasetId, vehicleId, vehiclesList, latch);
}
latch.await();
return vehiclesList;
}
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException {
ApiCallback<VehicleResponse> vehicleResponseApiCallback = new ApiCallback<VehicleResponse>() {
#Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders) {
vehiclesList.add(result);
latch.countDown();
}
};
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
}
Based on the research I've done so far, I think I have to mock the API calls using mockito? I'm still unclear on how the functionality can be unit tested.
These two statements are indeed the things that you want to isolate in your unit test:
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException {
....
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
...
}
1) Make you dependency mockable
But you can mock only something that you can set from the client side of the class.
Here the API is a local variable. So you should change your class to expose the dependency, for example in the constructor.
In this way you could mock it easily.
2) Make your mock not return a result but invoke the callback.
In a synchronous invocation context, you want to mock a returned result.
In an asynchronous invocation context with a callback, things are different. Indeed callbacks don't return to the caller but callbacks are invoked to provide the result of the invocation.So here what you want is that the mocked API invokes the onSuccess() callback with mocked parameters that represent the data set for your unit test :
#Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders) {
vehiclesList.add(result);
latch.countDown();
}
In your unit test you should mock in this way the callback for each expected invocation :
#Mock
VehiclesApi vehiclesApiMock;
// ...
// when the api method is invoked with the expected dataSetId and vehicleId
Mockito.when(vehiclesApiMock.vehiclesGetVehicleAsync(Mockito.eq(datasetId), Mockito.eq(vehicleId),
Mockito.any(ApiCallback.class)))
// I want to invoke the callback with the mocked data
.then(invocationOnMock -> {
ApiCallback<VehicleResponse> callback = invocationOnMock.getArgument(2);
callback.onSuccess(mockedVehicleResponse, mockedStatusCode,
mockedResponseHeaders);
return null; // it is a void method. So no value to return in T then(...).
});
I think that a cast is missing for ApiCallback but you should have the overall idea.
You are right: since you want to test your unit (i.e. the presented code), you should mock the API (mainly: the vehicleApi instance).
As-is right now, there is no way to inject a mocked instance of VehicleApi in your code (well, there is, but it would involve use of reflection... let's not go down this road). You can apply Inversion of Control to make your code testable: instead of constructing a VehicleApi within your object, write a constructor that expects a VehicleApi-instance:
public class YourClass {
private final VehicleApi vehicleApi;
public YourClass(final VehicleApi vehicleApi) {
this.vehicleApi = vehicleApi;
}
[...]
}
What have you won? Well, now you can inject a mocked object into your unit under test:
#RunWith(MockitoJRunner.class)
public class YourClassTest {
private final VehicleApi vehicleApiMock = mock(VehicleApi.class);
private final YourClass underTest = new YourClass(vehicleApiMock);
#Test
void someTest() {
// GIVEN
[wire up your mock if necessary]
// WHEN
[write the test-call]
// THEN
[verify that the unit under test is in the expected state]
}
}
This example assumes JUnit5 as testing- and Mockito as mocking-framework, but there are other options as well.
The test is written in Gherkin language:
- the GIVEN block describes the preconditions, i.e. in which the unit under test and the external (mocked) system(s) are in
- the WHEN block executes the action that should be tested
- the THEN block validates that the unit under test is in the expected state.
I have 2 tests which call the same code but check different things.
The first one uses doAnswer() to stub a method call and put parameters checker.
The second one doesn't need it.
But when I run my tests, the second test fails because fails the checker that was injected in the first test. And if I run only the second test it passes.
So, should I reset doAnswer() stub with empty checker im my setUp() method? Why so?
This is a checker that I use in the first test:
final Map<String, String> expectedParams = new HashMap<>();
expectedParams.put("param1", "A");
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
String name = (String) args[0];
Assert.assertEquals("event1", name);
Map<String, String> params = (Map<String, String>) args[1];
Assert.assertEquals(expectedParams, params);
return null;
}}).when(mMyMockedObject).myTestedMethod(anyString(), anyMap());
and this is the beginning
#Mock
MyMockedClass mMyMockedObject;
#Before
public void setUp() throws Exception {
System.out.println("setUp()");
MockitoAnnotations.initMocks(this);
}
In the second test I never call doAnswer() but my checker is called I don't know why :(
Adding the whole test.
#RunWith(LocalRobolectricTestRunner.class)
#Config(manifest = Config.NONE)
public class MyTest {
private static final String MAIN = "main";
#Mock
MyMockedClass mMyMockedObject;
#Before
public void setUp() throws Exception {
System.out.println("setUp()");
MockitoAnnotations.initMocks(this);
// this will connect MAIN with mMyMockedObject
SomClassICantShareNda.someMethodNda(MAIN, mMyMockedObject);
}
Hey, this is where I understand that this is my fault, not Mockito.
This is where I add new mMyMockedObject but forget to erase old instance of mMyMockedObject (inside SomClassICantShareNda, in static collection). So, after each test new mMyMockedObject will be created and added to collection.
Sorry, that was my fault.
The code duplicated many mocked object, cached them in a static collection and didn't reset it in setUp()
I'm starting some testing using Mockito on some service classes I use for connecting to my data store. I now want to determine the best practice way writing tests for it. The principle is for each entity there is a way to list, add, delete etc a row from the data store(mongo/mysql etc) for a specific entity.
Take this class that allows me to talk to my database that stores a list of companies
public class CompanyService extends Service{
public CompanyService() {
...
}
public saveCompany(Company company) {
...
}
// get a list of all companies
public List<Company> getCompanies() {
List<Company> companies = new ArrayList<Company>();
try {
CompanyResult<Rows<String, String>> rows = db.query(....);
for (Row<String, String> row : rows.getResult()) {
companies.add(row.getColumns());
}
catch (Exception e){
logger.warn("Error retrieving companies", e);
}
}
}
What exactly should I test in the getCompanies method and how can I use Mockito to do it?
Your System Under Test is the Company Service. You want to test that, assuming all its dependencies/collaborators function properly, it functions properly.
The db object looks like the only dependency/collaborator you need to worry about within the getCompanies() method. Use Mockito to mock the call to db.query()
You could set up a test method like so:
#Test
public void testGetCompanies() {
/*** Arraign ***/
CompanyService cs = new CompanyService();
// Setup mock db
DB mockDb = mock(DB.class);
// Setup fake results for query
CompanyResult<Rows<String, String>> sampleResults = ... // build sample results here
// Have query on mock return fake results
when(db.query(/* match arguments */)).thenReturn(sampleResults);
// Tell your System Under Test to use the mock collaborator
cs.setDB(mockDb);
/*** Act ***/
CompanyResult<Rows<String, String>> results = cs.getCompanies();
/*** Assert ***/
... // Test that results and sampleResults are effectively the same
}
You could test how your code would work if the db query returned and empty result,null or if row has null values or unexpected values. You could mock the db class to return these values. You could also mock the db class to throw an exception to see how your code could react.