Currently in school we are working on a rather large project. However testing in Java wasn't really explained that well so I didn't really work TDD like I was suppose to.
protected EntityManager getEntityManager() {
return EntityController.getEntityManager();
}
// Get all exam skeletons from the DB
#Override
public List<ExamSkeleton> getAllSkeletons() {
EntityManager entityManager = getEntityManager();
try {
TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);
List<ExamSkeleton> skeletons = query.getResultList();
return skeletons;
} catch (IllegalArgumentException exception) {
LOGGER.error(exception);
}
return Collections.emptyList();
}
So my question is, how do I test this method using Mockito?
Approach 1: Test the Code As Is
The getEntityManager method is private and it invokes a static method so, as things stand, you would need to use PowerMockito to provide a mocked instance of EntityManager in your test. For example:
#RunWith(PowerMockRunner.class)
#PrepareForTest({EntityController.class})
public class SomeTest {
#Test
public void aTest() {
PowerMockito.mockStatic(EntityController.class);
EntityManager entityManager = Mockito.mock(EntityManager.class);
Mockito.when(EntityController.getEntityManager()).thenReturn(entityManager);
TypedQuery<ExamSkeleton> query = (TypedQuery<ExamSkeleton>) Mockito.mock(TypedQuery.class);
Mockito.when(entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s")).thenReturn(query);
List<ExamSkeleton> expected = new ArrayList<>();
Mockito.when(query.getResultList()).thenReturn(expected);
ExamRepository examRepository = new ExamRepository();
List<ExamSkeletons> actual = examRepository.getAllSkeletons();
// this assertion verifies that getAllSkeletons gives you the result of the above SQl query
assertSame(expected, actual);
}
}
Approach 2: Refactor For Separation of Concerns and Ease of Testing
However, you could simplify things, from a testing and design perspective, by externalising the creation of the entity manager into a factory, for example.
public class EntityManagerFactory {
public EntityManager create() {
return EntityController.getEntityManager();
}
}
Next, inject an instance of EntityManagerFactory into whatever class contains getAllSkeletons() (i.e. the class you are testing). The simplest way of doing this is to declare it as a constructor argument:
public class SomeDao {
private final EntityManagerFactory entityManagerFactory;
public SomeDao(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Override
public List<ExamSkeleton> getAllSkeletons() {
try {
TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);
List<ExamSkeleton> skeletons = query.getResultList();
return skeletons;
} catch (IllegalArgumentException exception) {
LOGGER.error(exception);
}
return Collections.emptyList();
}
}
Now, you can test this code using vanilla mockito. For example:
public class SomeDaoTest {
#Test
public void canGetAllSkeletons() {
EntityManagerFactory entityManagerFactory = Mockito.mock(EntityManagerFactory.class);
Mockito.when(entityManagerFactory.create()).thenReturn(entityManager);
SomeDao sut = new SomeDao(entityManagerFactory.class);
// now SomeDao will use your mocked EntityManager so you can set expectations
// on createQuery etc to drive your test scenarios
// ...
}
}
1) The EntityManager should not be associated to the controller :
return EntityController.getEntityManager();
In terms of design, it is not desirable : low layer and high layer should not be mixed, otherwise why use them ?
In terms of testing for getAllSkeletons(), this coupling will also make the unit test harder to set and to write.
2) The actual method doesn't have logic to test but the exception case : you just create a query, execute it and return the result.
It is a good case for an integration test (without mocking the DB layer), less for an unit test.
As it will make the unit test complex and with not a lot of value.
Example of what you could get with Mockito and that I don't recommend.
Make the EntityManager a dependency of the class under test : with injection or not.
In your unit test, mock this dependency.
It could look like :
#Mock
EntityManager entityManagerMock;
#Test
public void getAllSkeletons(){
TypedQuery<ExamSkeleton> queryByMock = (TypedQuery<ExamSkeleton>) Mockito.mock(TypedQuery.class);
Mockito.when(entityManagerMock.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s"))
.thenReturn(queryByMock);
List<ExamSkeleton> skeletons = new ArrayList<>();
Mockito.when(queryByMock.getResultList())
.thenReturn(skeletons);
Foo foo = new Foo();
foo.setEntityManager(entityManagerMock);
// action
List<ExamSkeleton> actualSkeletons = foo.getAllSkeletons();
// assertion
Assert.assertSame(skeletons, actualSkeletons);
}
Really don't write this kind of code that just describes the flow of invocations.
It makes just a test brittle that will very rarely catch regressions.
Related
Hi have read a lot about this but can't come to a conclusion about the best way to test a method that is dependent on other method call results to perform its actions.
Some of the questions I've read include:
Testing methods that depend on each other
Unit testing a method that calls other methods
Unit testing a method that calls another method
Some of the answers sugest that we should only test the methods that perform only one action and then test the method that call this methods for conditional behaviuour (for example, verifying if a given method was called or not) and that's fine, I get it, but I'm struggling with other scenario.
I have a service with a REST api.
The controller has a create method that receives a DTO and calls the Service class create method with this argument (DTO).
I'm trying to practice TDD and for this I use this project I'm building without a database.
The code is as follows:
#Service
public class EntityService implements FilteringInterface {
private MemoryDatabase db = MemoryDatabase.getInstance();
//Create method called from controller: receives DTO to create a new
//Entity after validating that it's name and code parameters are unique
public EntityDTO create(EntityDTO dto) throws Exception {
validateUniqueFields(dto);
Entity entity = Entity.toEntity(dto, "id1"); //maps DTO to Entity object
db.add(entity);
return new EntityDTO.Builder(entity);//maps entity to DTO
}
public void validateUniqueFields(EntityDTO dto) throws Exception {
Set<Entity> foundEntities = filterEntityByNameOrCode(dto.getName(),
dto.getCode(), db.getEntities());
if (!foundEntities.isEmpty()) {
throw new Exception("Already exists");
}
}
}
This is the interface with methods reused by other service classes:
public interface FilteringInterface {
default Set<Entity> filterEntityByNameOrCode(String name, String code, Set<Entity> list) {
return list.stream().filter(e -> e.getSiteId().equals(siteId)
&& (e.getName().equals(name)
|| e.getCode().equals(code))).collect(Collectors.toSet());
}
default Optional<Entity> filterEntityById(String id, Set<Entity> list) {
return list.stream().filter(e -> e.getId().equals(id)).findAny();
};
}
So, I'm testing this service class and I need to test the create() method because it can have different behaviors:
If the received DTO has a name that already exists on the list of entities -> throws Exception
If the received DTO has a code that already exists on the list of entities -> throws Exception
If the received DTO has a name and a code that already exists on the list of entities -> throws Exception
If name and code are different, than everything is ok, and creates the entity -> adds the entity to the existing list - > converts the entity to DTO and retrieves it.
Problem:
To test any of the scenarios, suppose, scenario 1: I need to make the filterEntityByNameOrCode() method return a list with an Entity that has the same name as the Entity I'm trying to create. This method is called inside validateUniqueFields() method.
Problem is: I can't call mockito when() for any of this methods because, for that, I would have to mock the service class, which is the class that I'm testing and, thus, it's wrong approach.
I've also read that using Spy for this is also wrong approach.
So, where thus that leaves me?
Also: if this code is not the correct aprocah, and thats why
it can't be correctly tested, than, whats should the correct approach be?
This service will have other methods (delete, update, etc.). All of this methods will make use of the FilteringInterface as well, so I will have the same problems.
What is the correct way of testing a service class?
I would apply an DI pattern in your service, in order to mock and control the db variable.
#Service
public class EntityService implements FilteringInterface {
private Persistence db;
public EntityService(Persistence db) {
this.db = db;
}
}
After that, you will be able to add entities to Set accordingly to your scenarios
#ExtendWith(MockitoExtension.class)
class EntityServiceTest {
#Mock
private Persistence persistence;
#InjectMocks
private EntityService entityService;
#BeforeEach
void before() {
final Set<Entity> existentEntity = Set.of(new Entity(1L,1L, "name", "code"));
when(persistence.getEntities()).thenReturn(existentEntity);
}
#Test
void shouldThrowWhenNameAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "name", "anything");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldThrowWhenCodeAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "anything", "code");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldThrowWhenNameAndCodeAlreadyExists() {
final EntityDTO dto = new EntityDTO(1L, "name", "code");
assertThrows(RuntimeException.class, () -> entityService.create(dto));
}
#Test
void shouldNotThrowWhenUnique() {
final EntityDTO dto = new EntityDTO(1L, "diff", "diff");
final EntityDTO entityDTO = entityService.create(dto);
assertNotNull(entityDTO);
}
}
I'm trying to mock some data with Mockito for some tests, but I can't get it to work. My problem is that the value I want to mock is the return of a call inside the method I need to test.
I have a class, MyAccessor.java (that implements a service, MyService.java) that inside has a method, getMyMethod(String value), and inside that method there is a call to the DB:
final List<MyType> results = this.jdbcTemplate.query(sqlQuery, new RowMapper<MyType>() {
#Override
public MyType mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final int fieldA = rs.getInt("column_name_1");
final int fieldB = rs.getInt("column_name_2");
final int fieldC = rs.getInt("column_name_3");
final int fieldD = rs.getInt("column_name_4");
return new MyType(fieldA , fieldB , fieldC , fieldD);
}
}, value);
There is a case where due to timeouts with the database, an empty list is returned by the rowMapper. So in that case it is not possible to do return results.get(0) because you will get an ArrayIndexOutOfBoundsException.
So I have a check:
if (!results.isEmpty()) {
return results.get(0);
} else {
return new MyType(0, 0, 0, 0);
}
Now, I want to test this particular scenario but I don't know how to mock the resultSet return so that the rowMapper returns an empty list. In my test class I have set:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { ".....", "....."})
#TestPropertySource(".....")
public class MyClassTest {
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyService myService;
#Before
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
#Test
public void myTestMethod() {
Mockito.when(this.jdbcTemplate.query("SELECT NULL LIMIT 0", new RowMapper<Object>() {
#Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new ArrayList<>();
}
})).thenReturn(new ArrayList<Object>());
MyType result = this.myService.getMyMethod("value");
assertEquals(0, result.getFieldA());
assertEquals(0, result.getFieldB());
assertEquals(0, result.getFieldC());
assertEquals(0, result.getFieldD());
}
}
But the test fails because the result returned is from the database instead of an empty list to test the timeout case.
I think the problem may be because I'm injecting the mock in the service interface instead of the implementation, but I wanted to know if it can be done somehow so I don't have to test the mock for each implementation of that interface.
Or maybe I am using the Mockito framework wrong and in that case, how can I test correctly?
Regads.
I think you want something more like:
#Test
public void myTestMethod() {
Mockito.when(this.jdbcTemplate.query(eq("SELECT NULL LIMIT 0"), Mockito.any(RowMapper.class))
.thenReturn(new ArrayList<Object>());
}
Reason is because when mocking, you were saying only to return the empty list if both arguments to the query is exactly as you defined. In your case, you don't care about the exact instance of RowMapper.
I have found the problem. I was injecting the mock in the interface and not in the implementation of the interface. So I had to change:
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyService myService;
to:
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyAccessor myAccessor;
#Autowired
private MyService myService;
And inside the test this:
MyType result = this.myService.getMyMethod("value");
to:
MyType result = this.myAccessor.getMyMethod("value");
I don't know if there is a better way to do this, without having to instantiate all the implementations that the service may have.
Regards.
I am trying to write a unit test for a existing function that updates a value of a DB field using EntityManager. My unit test should verify that the class updates the value correctly. The current code looks like this:
public class MyClass {
MyClass(EntityManager em) {
this.em = em;
}
void updateValue() {
em.getTransaction().begin();
TypedQuery query = em.createQuery("select a from ...", MyResult.class);
List<MyResult> results = query.getResultList();
MyResult result = results.get(0);
result.setInterestingValue(1);
em.getTransaction().commit();
}
}
My current approach for the unit testing:
#Mock EntityManager em;
#Mock TypedQuery query;
publid void test() {
MyClass myClass = new MyClass(em);
MyResult result = new MyResult();
ArrayList<MyResult> resultList = new ArrayList<>();
resultList.add(result);
when(em.getTransaction()).thenReturn(mock(EntityTransaction.class));
when(em.createQuery(anyString(), any())).thenReturn(query);
when(query.getResultList()).thenReturn(resultList);
myClass.updateValue();
assertEquals(1, result.getInterestingValue());
}
My unit test above verifies that the MyResult instance is updated. However, it doesn't verify that the value is actually updated through EntityManager. In other words, I'd like to test if EntityManager is correctly used in the existing code. For example, even if I call commit() before setInterestinvValue(1), my test will still pass. Is there a good way to test it?
As commented above, if you use mockito, the solution will be - use inOrder():
MyResult result = mock(MyResult.class);
EntityTransaction transaction = mock(EntityTransaction.class)
inOrder.verify(result).setInterestingValue(1);
inOrder.verify(transaction).commit();
My mockito method is hitting the real method rather than invoking the mocked method. Your inputs will be helpful
Java code.
public class CheckUser {
private final UserDao userDao;
public CheckUser (final String domain){
userDao = new UserDao(domain);
}
public IUser getExistingUser(){
if (userDao == null) {
throw new RuntimeException("userDao is null");
}
IUser existingUser = userDao.getExistingUser();
if (existingUser == null) {
throw new RuntimeException("ExistingUser is null");
}
return existingUser;
}
}
This is my JUnit test code.
#Test
public void testExistingUser() {
UserDao mockUserDao = mock(UserDao.class);
when(mockUserDao.getExistingUser()).thenReturn(getExistingTestUser());
}
private UserDao getExistingTestUser(() {
return ExistingUserImpl.Builder(). //withfield methods. build();
}
I am creating this mock object for only testing purpose. This just return the mocked MockedExistingUserImpl object which is implemented by IUser.
public class MockedExistingUserImpl implements IUser {
//fields
//overriding getter methods for all fields
//Builder for ExistingUserImpl
}
When ever I call userDao.getExistingUser() in my code, I am expecting to return the mocked Existing user object but it is hitting the real method and failing the test due to domain connection. We don't establish a domain connection to run Junits. Any inputs are appreciated. Thank you !
The answer is to read a tutorial about Mockito and follow that. You make the typical mistake: you create a mock object, but then you don't do anything so that your production code uses that mocked object.
Just doing a mock(YourClass) doesn't magically change that new() in your production code to return a mocked instance.
You need to inject that mocked instance into the code under test. For example by using the #InjectMock annotation.
For a good intro, see https://www.baeldung.com/Mockito-annotations for example.
And note: as written right now, you will have a hard time to use Mockito for your tests. Due to the direct call to new(), you would need PowerMock(ito) to test it. So: learn how to use Mockito, and then rework your production code to be easily testable. (turning to PowerMock would be the wrong strategy).
Your mistake is in broken 'Dependency injection' principle.
Don't use new operator - create UserDao at the level above and use injection.
public class CheckUser {
private final UserDao userDao;
public CheckUser (final UserDao usedDao) {
this.userDao = userDao;
}
public IUser getExistingUser() {
if (userDao == null) {
throw new RuntimeException("userDao is null");
}
IUser existingUser = userDao.getExistingUser();
if (existingUser == null) {
throw new RuntimeException("ExistingUser is null");
}
return existingUser;
}
}
Now you can test your code in the following way:
#Test
public void testExistingUser() {
UserDao mockUserDao = mock(UserDao.class);
when(mockUserDao.getExistingUser()).thenReturn(getExistingTestUser());
CheckUser checkUser = new CheckUser(mockUserDao);
IUser iUser = checkUser.getExistingUser();
// assertions here
}
private UserDao getExistingTestUser(() {
return ExistingUserImpl.Builder(). //withfield methods. build();
}
The following is my test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/ds-context.xml")
#WebAppConfiguration
public class PaidListTest {
#Autowired
PaymentService paymentService;
#Test
public void getPaidList() {
List<PaymentGetServiceDO> response = null;
try {
response = paymentService.setPaidStatusList();
if(response != null && response.size() > 0){
for(int i = 0; i < response.size(); i++){
assertNotNull(response.get(i).getAgentcode());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the DAO layer of the service call paymentService.setPaidStatusList() have some operations with database activities of save and update, like em.merge(renewalPoliciesDO); But I don't want to execute them while calling the test method, they needs to get called only when actual business logic is called. How can I restrict or rollback the database transactions here?
The service and DAO methods are tedious here. However, I have simplified them for your reference. Service method
if(!updateList.isEmpty()){
HashMap<String,String> recordset = new HashMap<String,String>();
recordset = paymentDAO.setRenewalStatus(updateList);
}DAO implementation
if(paymentUpdateResDO.getPaymentstatus().equalsIgnoreCase("MANUAL") &&
!responseUpdateStatus.getPolicystatusid().equals(renewedStatusId)){
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Integer> payQuery = criteriaBuilder.createQuery(Integer.class);
Root<PaymentStatusDO> payRoot = payQuery.from(PaymentStatusDO.class);
payQuery.multiselect(payRoot.get("paymentstatusid"));
payQuery.where(criteriaBuilder.equal(payRoot.get("paymentstatusdescription"), "Manual"));
Integer paymentStatusId = em.createQuery(payQuery).getSingleResult();
insertOldPolicy(responseUpdateStatus);
responseUpdateStatus.setNewpolicyno(paymentUpdateResDO.getNewpolicyno());
responseUpdateStatus.setPreviousstatusid(responseUpdateStatus.getPolicystatusid());
responseUpdateStatus.setPolicystatusid(renewedStatusId);
Timestamp modifiedDate = new Timestamp(System.currentTimeMillis());
responseUpdateStatus.setModifieddatetime(modifiedDate);
responseUpdateStatus.setModifiedby("RPA");
responseUpdateStatus.setPaymentstatusid(paymentStatusId);
responseUpdateStatus.setActiveindicator("Y");
em.merge(responseUpdateStatus);
successRecords++;
}
In my case, I need the result arraylists, but the em.merge and em.persist activities need to be ignored.
When I try with MockitoJUnitRunner as #GauravRai1512 preferred, I get my testcase executed but the program is terminated so that I am unable to get the result ArrayLists.
Refer this image
You should follow below approach as suggested by Pooja Aggarwal.
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class PaidListTest {
#Mock
PaymentService paymentService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mapperObj = new ObjectMapper();
List<PaymentGetServiceDO> response = new ArrayList<PaymentGetServiceDO>();
}
#Test
public void getPaidList() {
List<PaymentGetServiceDO> response = null;
try {
response = paymentService.setPaidStatusList();
if(response != null && response.size() > 0){
for(int i = 0; i < response.size(); i++){
assertNotNull(response.get(i).getAgentcode());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
This code will not execute your save and update operation.
You can use mocking of the database calls. You can write test cases using Jmockit where you can mock the call of database which will prevent save or update operation in the database.
You can learn writing jmockit test cases from http://jmockit.github.io/tutorial/Introduction.html
Or
https://winterbe.com/posts/2009/08/18/introducing-jmockit/
Like others have said, you can either:
Mock the PaymentService so that you can define exactly what to do on each of the calls to it. In this case, you would simply return a hard-coded List<PaymentGetServiceDO> response, and your test could do something with that. The downside of this is that you're not really testing PaymentService in that case. You're simply looking at hard-coded data, and doing something with it. In my view, that's not a valid test.
Or, you can instantiate a PaymentService object with either an in-memory database, only for use with tests, or with a mock DB object, where you can then define exactly how to respond to each of the merge, save, update and similar operations. If the PaymentService object doesn't allow this, then it should be redesigned so it accepts as a parameter a Database/Connection, which would be the normal #Autowired injected parameter when running in production, but which you can instantiate manually when testing. This would be a proper test, since you'd be validating the rest of the logic inside setPaidStatusList, but you're not actually hitting a database to achieve that.