Decoupling a Mockito test from a specifc test value? - java

I am trying to test the method findById() method in the class below that reads data from my Database using the CrudRepository:
Class under test:
public interface PersonRepository extends CrudRepository<Person, Integer>
{
Person findById(String id);
}
Below is my test class, the test is currently passing but I would like to change it so that if the id "1" I am testing with is removed from my database, I can still run my test. I.e. do not rely on data within the database.
How can I do so?
Test Class:
public class PersonRepositoryTest {
#Mock
private PersonRepository personRepository;
#Before
public void setUp() throws Exception {
//Initialize the mocked class and ensure that it is not null
MockitoAnnotations.initMocks(this);
assertThat(personRepository, notNullValue());
}
#Test
public void testFindById() throws ParseException {
//test string
String id = "1";
//when it is called, return
when(personRepository.findById(anyString())).thenReturn(new Person());
Person person = personRepository.findById(id);
assertThat(person, notNullValue());
}
}

As mentioned in the Post comments by #Thomas, you are just mocking the database. I'm assuming you want to write a negative test case when the ID is 1.
You can just return null, instead of person Object. Instead of Matchers, pass a specific value to differentiate your positive and negative test cases.
Positive Case -
when(personRepository.findById(2)).thenReturn(new Person());
Negative Case -
when(personRepository.findById(1)).thenReturn(null);

Related

Right way of testing method that depends on other method calls

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);
}
}

Junit Test: findById method of the Repository

I am new in Junit tests and I have a question about it. Here you can see the method findById in my service class:
#Service
public class DefaultQuarterService implements QuarterService {
private final QuarterRepository quarterRepository;
public DefaultQuarterService(QuarterRepository quarterRepository) {
this.quarterRepository = quarterRepository;
}
#Override
public QuarterEntity findById(int id) {
return quarterRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("Quarter does not exist for id = %s!", id)));
}
}
And here is my QuarterRepository:
#Repository
public interface QuarterRepository extends CrudRepository<QuarterEntity, Integer> {
}
And here is my Junit implementation for this method:
#MockBean
private QuarterRepository quarterRepository;
#Test
public void throwExceptionWhenQuarterIdNotFound() {
int id = anyInt();
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
assertThatAnExceptionWasThrown(String.format("Quarter does not exist for id = %s!", id));
}
public void assertThatAnExceptionWasThrown(
String errorMsg
) {
expectException.expect(RuntimeException.class);
expectException.expectMessage(errorMsg);
}
Unfortunately test doesn't pass. Here the error in terminal:
java.lang.AssertionError: Expected test to throw (an instance of
java.lang.RuntimeException and exception with message a string
containing "Quarter does not exist for id = 0!")
Maybe it is so simple but I can not see what I am missing. I would be so happy if you can direct me. Thanks a lot!
As you mock your Repository it will return with Optional.empty() correctly, I think you should call your service's (which is Autowired) findById method. It will throw the exception actually.
First issue
In the assertThatAnExceptionWasThrown method you expect RuntimeException BUT in the service class you throw EntityNotFoundException, So I guess you should expect EntityNotFoundException in your test case.
Second issue
After this part of the code.
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
Why didn't you call your service method (findById)?
When you are returning the empty value, you should verify your condition with the service method you want to test it.
It should be something like this.
assertThatThrownBy(() -> defaultQuarterService.findById(id))
.isInstanceOf(ApiRequestException.class)
.hasMessageContaining("PUT_YOUR_EXCEPTION_MESSAGE_HERE");
This is a good sample for unit-test in the spring boot. You can check it out. Link
Try the above solutions and let me know it has been fixed or not. Good luck

Mockito mock same method calls with different collection-arguments

I try to mock same method calls with different collection-arguments.
My problem is that im not getting the correct mocked-answer from Mocked-Call for the input.
Test-Class:
#ExtendWith(SpringExtension.class)
public class CollectionTest {
#MockBean
private Controller c;
#BeforeEach
public void init() {
Collection<String> a = Mockito.anyCollection();
a.add("a");
Mockito.when(c.run(a)).thenReturn("a");
Collection<String> b = Mockito.anyCollection();
b.add("b");
Mockito.when(c.run(b)).thenReturn("b");
}
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // DOESNT'WORK!!! Returns "b" but should "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); //
}
}
Controller-Class:
#Service
public class Controller{
public String run(Collection<String> c) {
return "not-mocked";
}
}
I'v got no idea why it doesn't return "a". I tried to change the collection to string but same behaviour.
What are the Steps to do, to get the following behaviour?
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // should return "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); // should return "b"
}
Im using Java Mockito "3.1" and Spring, but I think Mockito is the important information here.
Your second call - Mockito.when(c.run(b)).thenReturn("b");
is overruling our first call so Mockito will therefore always return "b".
If you need multiple answers from the same call, you can use the varags variant:
when(c.run(anyCollection())).thenReturn("a", "b");
Now the first call to the controller's run method will return "a" and all subsequent calls will return "b". You can provide as many return results as you want and the last one will be repeated from then on as the answer.
Write two tests will show you the results you are expecting.
You are adding to the same Controller two different results so you get only the last one : Mockito.when(c.run(b)).thenReturn("b");
Normal. The last mocked expected result in your setUp() will stay in memory.
Previous answer was :
You can use something like junit and mockito to test your spring-web-mvc application.
It looks like that :
#WebMvcTest(controllers = UserController.class)
#ActiveProfiles("test")
class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService userService;
private List<User> userList;
#BeforeEach
void setUp() {
this.userList = new ArrayList<>();
this.userList.add(new User(1L, "user1#gmail.com", "pwd1","User1"));
this.userList.add(new User(2L, "user2#gmail.com", "pwd2","User2"));
this.userList.add(new User(3L, "user3#gmail.com", "pwd3","User3"));
}
}
And as an example :
#Test
void shouldFetchAllUsers() throws Exception {
given(userService.findAllUsers()).willReturn(userList);
this.mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(userList.size() )));
}
Example from #see https://medium.com/backend-habit/integrate-junit-and-mockito-unit-testing-for-controller-layer-91bb4099c2a5

why is mockito not called when executing mocked method?

Here is my test:
#Test
public void myTest() throws Exception {
String aVenueId = "1.2.3";
Venue2 aVenue = new Venue2(aVenueId);
VenuesRepository repository = mock(VenuesRepository.class);
when(repository.getVenue(anyString())).thenReturn(new VenueThumb(aVenue));
aMethodWithInnerCallToRepositoryGetVenue();
...
}
However when test runs my real code,
this line returns null (as there is no venue with venueId = "1.2.3"
that's why i used my mock for the first place).
public void aMethodWithInnerCallToRepositoryGetVenue(){
...
IVenue v = repository.getVenue(r.venueId);
..
}
You mocked a repository in your method, but this mock repository is assigned to a local variable only. So the code calling repository.getVenue() calls it on a different repository instance, and not on the one created by Mockito:
aMethodWithInnerCallToRepositoryGetVenue();
There's no way this method knows about the local variable initialized just before the method call.
As the user JB Nizet has written you are using two different instances of repository. You can do something like this: first you have a repository class you want to mock being a collaborator in the next class.
public class VenuesRepository {
public IVenue getVenue(String id) {
return new IVenue() {
// do something
};
}
}
Class Foo uses an instance of VenuesRepository as a collaborator which is not the objective of your unit test so you should mock its behavior when required.
public class Foo {
private VenuesRepository repository;
public Foo(VenuesRepository repo) {
repository = repo;
}
public IVenue getVenueFromRepository(String id) {
return repository.getVenue(id);
}
}
Now the test
public class FooTest {
#Test
public void testGetRepositoryWithMock() throws Exception {
String aVenueId = "1.2.3";
VenuesRepository repository = mock(VenuesRepository.class);
when(repository.getVenue(anyString())).thenReturn(mock(IVenue.class));
Foo foo = new Foo(repository);
assertNotNull(foo.getVenueFromRepository(aVenueId));
}
}
Now the instance of repository which has been mocked is the same.
I hope it helps.

Test a method which calls another method of the object in mockito

I have an interface, ex:
interface MyService {
void createObj(int id)
void createObjects()
}
I want to test an implementation of the createObjects method, which has body like:
void createObjects() {
...
for (...) {
createObj(someId);
}
}
I have already tested createObj(id):
#Test public void testCreate() {
//given
int id = 123;
DAO mock = mock(DAO.class);
MyService service = new MyServiceImpl(mock);
//when
service.createObj(id);
//verify
verify(mock).create(eq(id));
}
So I don't want to repeat all test cases for it in the test for createObjects.
How can I make sure that another method of the real object was called besides the one I am testing?
Use a spy:
MyService myService = new MyServiceImpl()
MyService spy = spy(myService);
doNothing().when(spy).createObj(anyInt());
// now call spy.createObjects() and verify that spy.createObj() has been called
This is described, like everything else, in the api doc.

Categories