How to write mockito test cases for factory class - java

I was new for writing test cases. I want to write the Mockito test cases for the below :
#Component
public class FactoryClass {
#Autowired
private DataService dataService;
#Autowired
private ModelMapper modelMapper;
public List<TestEntity> convertEventToEntity(CEvent cEvent, Event event){
List<TestEntity> TestEntityList =new ArrayList<>();
if (!CollectionUtils.isEmpty(cEvent.getOrderDetails())) {
for (CEventDetail cEventDetail : cEvent.getCEventDetail()) {
log.info("Creating TestEntity entity ..");
TestEntity testEntity = new TestEntity();
testEntity.setEId(event.getEId());
testEntity.setActId(event.getHeaderReference().getActId());
testEntity.setEName(event.getEType());
tfbUpgrades.setPO(cEventDetail.getPO());
TestEntityList.add(testEntity);
}
}
return TestEntityList;
}
}
Can anyone please help me with the code sample to write mockito test cases for the factory class.

The only thing you might want to test in this class is the public convertEventToEntity method. The class has two dependencies - dataService and modelMapper. But they are not used in the code, so you don't need to mock them. So you can write tests for this class without using Mockito. Just create an object of FactoryClass and call the method with some input CEvent and Event objects. You can assert on expected List<TestEntity>.
You can test several logical paths in this method. For example, there's an if condition that does something only if cEvent's order details are present. So you can have two test cases where you pass cEvent with and without order details and you can verify that the code is executed correctly in both cases.

Related

Mockito mock returning Null value from repository

I'm trying to test my service implementation with JUNIT5. I don't quite understand whats going wrong but my UserRepository doesn't seem to be returning a value.
I have tested to see if the UserRepository has been used with:
verify(repository, times(1)); And the response was "Wanted but not invoked...Actually, there were zero interactions with this mock"
Here is the Test Class:
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
UserRepository repository;
#InjectMocks
UserServiceImpl userServiceImpl;
UserModel inputUserModel;
#BeforeEach
public void setUp() throws Exception {
inputUserModel = new UserModel();
inputUserModel.setEmail("testemail#gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
User inputUser = User.builder()
.email(inputUserModel.getEmail())
.firstName(inputUserModel.getFirstName())
.lastName(inputUserModel.getLastName())
.password(inputUserModel.getPassword())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1)
.build();
User outputUser = User.builder().
email(inputUserModel.getFirstName()).
password(inputUserModel.getLastName()).
lastName(inputUserModel.getFirstName())
.firstName(inputUserModel.getFirstName())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1).build();
Mockito.when(repository.save(inputUser)).thenReturn(outputUser);
}
#Test
public void whenSaveUser_ThenUserHasID(){
Assertions.assertEquals(1, userServiceImpl.saveUser(inputUserModel).getUserID());
}
}
The error that I am getting:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'save' method:
repository.save(
User(userID=0, email=testemail#gmail.com, timeCreated=2022-12-14 03:18:24.0435578, password=test, firstName=john, lastName=doe)
);
-> at com.jschwery.securitydemo.service.Implementations.UserServiceImpl.saveUser(UserServiceImpl.java:37)
- has following stubbing(s) with different arguments:
1. repository.save(
User(userID=1, email=testemail#gmail.com, timeCreated=2022-12-14 03:18:24.0235563, password=test, firstName=john, lastName=doe)
);
My Service Class that I'm creating the test for:
public class UserServiceImpl implements UserService {
UserRepository userRepository;
#Autowired
public UserServiceImpl(UserRepository repository){
this.userRepository = repository;
}
#Override
public User saveUser(UserModel userModel) {
if(!Objects.equals(userModel.getPassword(), userModel.getMatchPassword())){
throw new UserException("Passwords do not match");
}
User user = User.builder().
email(userModel.getEmail()).
firstName(userModel.getFirstName()).
lastName(userModel.getLastName()).
password(userModel.getPassword()).
timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).build();
User returnedUser = userRepository.save(user);
System.out.println(returnedUser.getEmail());
System.out.println("userID" + returnedUser.getUserID());
return returnedUser;
}
}
Thanks for reading! :)
I guess that your User class has an equals/hashCode defined either on the userID field or on all fields. When you mock your repository in your setUp method, you define what the method should do when it is called with the exact object that you specified, comparing the given object to the object specified in the mock by calling the equals/hashCode method.
You define the User object in your setUp method to have the userID 1 but in your UserServiceImpl the userID is not ever set (as it is generated by the persistence layer). Therefore the equals/hashCode check to determine if Mockito should execute the stub logic will never be called because the object passed by the UserServiceImpl will never be equals to the one that you defined in your mock.
There are several ways how you can solve this.
1) Integration test
My advice would be to convert your Unit-Test to a #SpringBootTest. There is no value in testing mocked behaviour. If this test was an integration test, you would test that the repository inserts the User into the database and generates an ID with your assertion.
2) Unit test
If you want to mock the repository, you should use an any() matcher for the input argument and then do what the repository would do, namely set the id:
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
I would then assert, that the userID is not null, as the userID will be randomly generated as it would in production:
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
private UserRepository repository;
#InjectMocks
private UserServiceImpl userServiceImpl;
#Test
void saveUser_userHasID(){
// Arrange
final UserModel inputUserModel = new UserModel();
inputUserModel.setEmail("testemail#gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
// Act
final User user = userServiceImpl.saveUser(inputUserModel);
// Assert
assertThat(user.getUserID()).isNotNull();
}
}
I highly recommend to use AssertJ for your assertions as it offers fluent asssertions and way more assertion methods than JUnit.
tl;dr
Spring data can't generate a userID through a simple unit test with a mock, you will need a spring context running in an Integration test.
Details
There are two fields leading to the error you are having : UserID and timeCreated.
For the timeCreated field : in your test you create a inputUser
object that you use for your mock. the problem is that this object
inputUser is NOT the one used by your method under test
saveUser(UserModel userModel) you could clearly see the that the
timestamp differs in the erreor between the two objects.
For the UserID field : again you give your expect your mock to use
the inputUser object. this object has userID = 1. but in the real
method saveUser(UserModel userModel) this field is not set
explicitly so the default value will be set to 0 you could see that
also in the error.
Now since your test case whenSaveUser_ThenUserHasID is meant to verify that a saved record has an ID, you can't do it with a mock. The ID is generated by the Spring Data you are using and yo make it run, you should use and Integration test to run your spring context in your test in order for your spring data to be able to generate an ID for your user, here is a previous question that will be helpful for you to do so. With this, you will neither need the inputUser in your test nor the mock. (You can use #DataJpaTest from JUnit 5 or #SpringBootTest...)

I am trying to Mock an autowired map of instances in spring but I get null pointer exception

In one of my controller
#Autowired
private Map<String, ABC> abcMap;
now I want mock it in one of the unit test but I always get null pointer exception.
This map contains implementations of ABC abstract class.
Can anyone suggest a possible solution?
I'm not sure what Unit test Framework you are using but there are ways of making it inject the mock details. You'll have to give us more information before before we can answer.
Personally I don't much like Autowired private fields, so at the risk of answering a different question can I suggest you consider using an Autowired constructor instead. From Springs POV it won't make a difference, your object will be create and all the appropriate data wired in. (OK, there is a slight change in the order things are done, but generally you won't notice). You will have to write a constructor to copy the constructor parameters to private fields, but:
Those fields could be made final, which could make your class safer
Your Unit tests wont need any 'magic' to initialise the Autowired fields - just pass parameters
If you refactor you class to remove add/remove/modify an Autowired field then you have to remember to change your test code. With an Autowired constructor you test code has to be changed or it won't compile, and your IDE might even help you do it.
Update
The Autowired constructor alternative looks something like:
#Controller
class MyClass {
private final Class1 bean1;
private final Object value2;
#Autowired
MyClass(Class1 bean1, Class2 bean2) {
this.bean1 = bean1;
this.value2 = bean2.getValue();
}
}
Keys points are:
The class has just one constructor and it requires parameters.
The fields are not annotated #Autowired, because Spring is not assigning values to them; the constructor does that.
The constructor IS annotated as #Autowired to tell Spring to pass the beans as parameters
The first parameter is stored in a final variable - you code can't accidentally over write it, so your code is safer
In my example the second parameter is only used in the constructor, so we don't have to store it as a field in your controller. I often to this if the Bean is an object that passes configuration around.
A No-argument constructor is not required
At test time your code will have to pass parameters to the class.
Your test code will look something like:
class MyClassTest {
private Class1 bean1;
private Class2 bean2;
private MyClass objectUnderTest;
#Before
public void setUp() throws Exception {
bean1 = mock(Class1.class);
bean2 = mock(Class2.class);
// Train mocks here
objectUnderTest = new MyClass(bean1, bean2)
}
#Test
public void myTest() {
// Do something with objectUnderTest
}
}
Key points are:
There are no #MockBean annotations
The Unit test is only using the API that your Controller bean defines; No black magic is required
It's not possible to create a MyClass with out providing the required data. This is enforced by the compiler
I think you can try it.
The sample of code:
public interface Animal {
}
#Service
public class Cat implements Animal{
}
#Service
public class Dog implements Animal{
}
#Service
public class Clinic {
#Autowired
private final Map<String, Animal> animalMap = new HashMap<>(2);
}
Sample of test
#Configuration
public class TestEnvConfig {
#Bean
public Clinic create(){
return new Clinic();
}
#MockBean // you can do it without mock or use #ComponentScan
Dog dog;
#MockBean
Cat cat;
}
#SpringBootTest(classes = TestEnvConfig.class)
#RunWith(SpringRunner.class)
public class ClinicTest {
#Autowired
private Clinic clinic;
}

Junit - mockito Wanted but not invoked:

Am writting test class for my service implementation class. That service impl class having communication with my repository interface for DB action.
Code is working properly, but while i was writting the test cases for this implementation and getting "wanter but not invoked" error from junit.
Please find my classes below.
Following is my Repository interface
public interface UserRepository extends JpaSpecificationExecutor<UserDeactivationThreshold>,CrudRepository<UserDeactivationThreshold,Long> {
#Query("SELECT bt from BusinessTypes bt where bt.code = :businessTypeCode")
BusinessTypes findByBusinessCodeId(#Param("businessTypeCode") String businessTypeCode);
}
Following is my Service interface
public interface UserDeactivationThresholdService {
public List<UserDeactivationThreshold> getThresholdValue(String businessTypeName);
}
Following is my Service Implementation class
#Service
#Transactional(readOnly = true)
public class UserDeactivationThresholdServiceImpl implements UserDeactivationThresholdService {
private UserRepository userRepository;
#Autowired
public UserDeactivationThresholdServiceImpl(UserRepository userRepository,SecurityClient securityClient, EmailDispatcherService emailDispatcherService) {
this.userRepository = userRepository;
}
#Override
public List<UserDeactivationThreshold> getThresholdValue(String businessTypeName){
return userRepository.findByBusinessType(businessTypeName);
}
}
Please find my Test class
#RunWith(MockitoJUnitRunner.class)
public class UserDeactivationThresholdServiceImplTest {
#Mock
private UserRepository userRepository;
#Mock private UserDeactivationThresholdServiceImpl userDeactivationThresholdServiceImpl;
#Test
public void shouldGetThresholdValueTest() {
UserDeactivationThreshold userDeactivationThreshold = make(a(UserDeactivationThresholdMaker.BCP));
when(userRepository.findByBusinessType("BCP")).thenReturn(asList(userDeactivationThreshold));
verify(userRepository, times(1)).findByBusinessType("BCP");
}
}
Following errors that am getting :
Wanted but not invoked:
userDeactivationThresholdRepository.findByBusinessType(
"BCP"
);
-> at com.core.service.impl.UserDeactivationThresholdServiceImplTest.shouldGetThresholdValueTest(UserDeactivationThresholdServiceImplTest.java:58)
Actually, there were zero interactions with this mock.
* Update *
I tried in this way also, but that also not worked.
verify(userDeactivationThresholdServiceImpl, times(1)).getThresholdValue("BCP");
Following error that i was getting.
-> at com.core.service.impl.UserDeactivationThresholdServiceImplTest.shouldGetThresholdValueTest(UserDeactivationThresholdServiceImplTest.java:58)
Actually, there were zero interactions with this mock.
* Update 2 *
I have added the below code in to my test class. But now am getting the different issue "The method is(Integer) is ambiguous for the type UserDeactivationThresholdServiceImplTest"
List<UserDeactivationThreshold> userDeactivationThresholdList = userDeactivationThresholdServiceImpl.getThresholdValue("BCP");
assertThat(userDeactivationThresholdList.size(), is(1));
Am getting the below messiage for is(1).
The method is(Integer) is ambiguous for the type UserDeactivationThresholdServiceImplTest
As others pointed out already you made the usual beginner mistakes:
Do not mock the class under test.
Make sure that your mocks are injected into your class under test, either by using annotations (#Mock, #InjectMocks) or by providing the references to the mock manually to the class under test.
Use the instance of your class under test in the test to actually test something.
Make sure to read the documentation and a tutorial (e.g. this, this or this) again.
There are also plenty of similiar question to be found here with answers that provide some code examples as well.

Test class with nested dependencies

I'm testing a class with nested (Autowired) dependencies. The class implements businesslogic for making alterations in the backend. Specifically the test should assert that when a certain backend call returns an error:
No more backend calls are made
The response object returns an error
I dont know how the do the latter.
My class looks something like this:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private SpecificUtil util1;
#Autowired
private GeneralUtil util2;
#Autowired
private Helper helper;
public Response doSomethingClever(Request request) {
// calls to dao
// logic with help of util and helper classes
}
}
The testclass:
public class HandlerTest {
#Spy
private DaoImpl dao;
#Mock
private SpecificUtil util1;
#Mock
private GeneralUtil util2;
#Mock
private Helper helper;
#InjectMocks
Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any(SpecificQuery.class))).thenReturn(error);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that either:
// Response has errors - fails
// because helper classes are mocks, have not set the error
assertNotNull(response.getErrorMessage());
// the method setErrors of Response was called once - fails
//because the setError was called earlier!
Response spyResponse = Mockito.spy(errorResponse);
verify(spyResponse, times(1)).setError(anyString);
//verify no other calls are made except the queryBackEnd call - this part works
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}
The Response object is created in the Handler class. If i check the returned response, no interactions will be recorded by Mockito, because the interactions have taken place prior to calling Mockito.spy.
I have tried making it an integration test, by using #Spy instead of #Mock. The idea is to instantiate all the nested dependencies except for the dao and get a proper Response to test for errors. But this does not work because some of the #Autowired helper and utility classes also have #Autowired dependencies and these nested dependencies are not instantiated during testing.
Is there any way to inject #Spy objects into other #Spy objects with Mockito?
Or is there some other solution in this case? Sould i write my own mockobjects?
A unit test should test the code of a specific unit only (here the Handler class). This includes interacting with the dependencies.
From what you wrote in your question and comments your handle method looks something along these lines:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private Util util;
public Response doSomethingClever(Request request) {
SpecificQuery specificQuery = new SpecificQuery();
specificQuery.setSomeData(request.getSomeData());
IntermidiateResponse intermidiateResponse = dao.queryBackEnd(specificQuery);
Response response = util.processIntermidiateResult(intermidiateResult);
return response;
}
}
There are a few interactions to unit test here:
Given a Request instance assert that DaoImpl::queryBackEnd method is called with a SpecificQuery instance that has someData property set to someData property from the Request object
Given a mocked IntermidiateResponse being returned from the DaoImpl::queryBackEnd method assert that this result is passed on to the Util::processIntermidiateResult method
Given a mocked Response being returned from the Util::processIntermidiateResult method assert that this is exactly what gets returned from the handle method
This way you have 100% coverage on the Handler::handle method. If you have some more calls in the response processing pipeline you test them all accordingly.
Hope this answers your question. Good luck
There are two options, one is to test the Handler in isolation, mocking everything else. See the answer by jannis. The other option is to control/mock the in- and output of the Handler, and treat the Handler and all its utility classes as a black box.
I have opted for this last option because it means that i can refactor the utility-classes, and the Handler class itself, and that tests will succeed as long as i don't change what the Handler does. It does mean that i have departed somewhat from doing unit testing and that i'm really doing more of an integration test.
For this i mock the Dao class, so that i can have it return an error at the desired point and so that i can assert that no further calls are made after the error.
It is the only class i mock, so i need to inject it into the Handler. This is possible with Springs ReflectionTestUtils (I did'nt know about this yesterday)
The testcode then becomes shorter:
public class HandlerTest {
#Autowired
private Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
Dao mockDao = org.mockito.Mockito.mock(DaoImpl.class);
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any (SpecificQuery.class))).thenReturn(error);
// inject the dao
ReflectionTestUtils.setField(handler, "dao", mockDao);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that Response has errors
assertNotNull(response.getErrorMessage());
//verify no other calls are made except the queryBackEnd call
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}

In a JUnit test is there a rule to set the subject of the test

I've been writing a lot of JUnit tests lately and see this same boilerplate pattern.
public class MathOpTest {
private MathOp a;
#Before
public void setUp(){
a = new MathOp();
}
...
}
Is there an annotation to set this up for me as I always need to write a setUp method and it usually only has the single class I'm testing.
Something like:
public class MathOpTest {
#TestSubject
private MathOp a;
...
}
You can assign the fields when they are declared:
public class MathOpTest {
private final MathOp mathOp = new MathOp();
...
}
This is simple and straight-forward, so I recommend that you assign fields in your test class at declaration time whenever possible (certainly in the case you gave).
If you want to understand a bit more, read on.
JUnit will create a unique instance of your test class for each test method, so even if your test modifies internal state of MathOp, using fields this way is safe as long as your tests don't modify global state.
For JUnit4-style tests (i.e. tests that do not extend junit.framework.TestCase) JUnit will create the test class just before the test method is run, and make it eligible for garbage collection after the test method completes.
Use #Before methods for more complex initialization.
Usually I use #Before when:
Initialization of the field is complex
Initialization of the field requires calling code that is declared to throw a checked exception
You need to do initialization after a #Rule has been applied (for instance, injecting a mock into a constructor)
Usually you would create the object under test in the test method when the class needs to be constructed different ways for different use cases.
Examples
Here is an example of using #Before and initMocks():
public class MathOpTest {
#Mock private Calculator mockCalculator;
#Mock private Supplier<Double> mockPreviousResultSupplier;
private MathOp mathOp;
#Before
public void createMathOp() {
MockitoAnnotations.initMocks(this);
mathOp = new MathOp(
mockCalculator, mockPreviousResultSupplier);
}
...
}
Here's an example of a #Before method that uses the result of a #Rule:
public class MyWriterTest {
#Rule public final TemporaryFolder folder = new TemporaryFolder();
private File output;
private MyWriter writer;
#Before
public void createMyWriter() {
output = folder.newFile();
writer = new MyWriter(output);
}
...
}
Aside: I personally wouldn't recommend using #InjectMocks to create the class you are testing. It's too much magic for my taste. Having an explicit constructor is cleaner and simpler, and I like my tests to be clear and simple :-)
Nothing like this directly exists in vanilla JUnit to my recollection. Most people elect to either initialize their test subject in a #Before statement, or inside of their tests. In its defense, it makes it clear what is being established before the tests are run, and it always resets the state of your test object.
If you're using Mockito, you actually do have the benefits of declaring a class and annotating it with #InjectMocks to both instantiate the class and inject whatever #Mock classes you had prior.

Categories

Resources