How to mock MongoDB repository method in Java - java

I'm trying to mock the following piece of code in my JUnit test
requestData = requestRepository.findByRequestId(requestId);
by doing
#Mock
RequestRepository requestRepository;
#Mock
RequestData requestData;
Mockito.when(requestRepository.findByRequestId(requestId)).thenReturn(requestData);
But instead of giving the mock object back, i'm getting null value.
What is the correct way to mock MongoDB repository methods.

When using SpringRunner based test, use #MockBean to declare mock of your context beans.

If you don't know about your request id value (it may be a dynamic value) on that case you can use Mock.any(<?>.class).
Example:
Mockito.when(requestRepository.findByRequestId(Long.class)).thenReturn(requestData);
The above example is only for requestId of type Long, if you need integer then you need to change the class to Integer.

Related

Generating unit tests for my service implementations on the spring boot application

after hours of tries and fails I come to you in hope of a solution.
I'm struggle making unit tests for my spring boot application. I'm using mockito and Junit 5.
My architecture is made out like this:
A controller
An interface of the service
A implementation of the service interface
A repository extending CrudRepository<Entity, Long>
For now I just want to test out my service implementation.
This is how it looks like for now :
`
#SpringBootTest public class ServiceImplTest{
#Mock
private Entity e;
#MockBean
private EntityRepository entityRepository;
#MockBean
private EntityService entityService;
#BeforeEach
init(){
e = new Entity();
e.name ="abc";
}
#Test
private simpleTest(){
// saving my element in the mocked repository
entityRepository.save(e);
// I have a repository query to delete an element in a specific way. I ask it to return 1 if it receives the order to activate this method
doReturn(1).when(entityRepository).specialDeleteEntity(1L);
// in the code serviceDeleteEntity() does some operations then calls entityRepository.specialDeleteEntity
int howMany = entityService.serviceDeleteEntity(1L);
// this fails because there was nothing in the repository to be deleted
assertEquals(howMany, 1);
}
}
I just have a feeling the the Mocked Repository is not connected to my Mocked Service and by this, the operations between them don't work.
I have also tried another solution where I didn't mock the repository , just in case :
#SpringBootTest class ServiceImplTest {
#MockBean
private EntityRepository mockEntityRepository;
#Autowired
private EntityService entityService;
#Test
void testDelete() {
// Given
final Entity entity = new Entity();
entity.name = "abc";
// Setup
when(mockEntityRepository.specialDeleteEntity(1L)).thenReturn(1);
// When
final int result = entityService.specialDeleteEntity(1L);
// Then
assertThat(result).isEqualTo(1);
verify(mockEntityRepository).specialDeleteEntity(1L);
}
}
I may lack some anotations or some methods maybe. I just want your advice on the problem and maybe a step towards the solution. Thank you very much.
There are a few issues with your test:
1. Wrong use of mocks
A mock is a dummy implementation of a class. You use them to bypass the normal behaviour of those classes. For example, if you write a unit test for EntityService, you don't want to set up an entire database and insert/delete mock data. So in that case you want to mock dependencies such as EntityRepository.
This means that in your EntityServiceTest you should only be mocking EntityRepository. You shouldn't mock Entity because this class normally doesn't contain a lot of behaviour and you shouldn't mock EntityService because you want to test the actual behaviour of this service and not of a mock.
2. Choose one mocking framework
Your test is currently using a combination of #Mock and #MockBean.
Both of these annotations allow you to mock a class.
The difference is that #Mock relies only on Mockito and #MockBean mocks the class and creates a Spring bean of it to use for autowiring.
This means that the #MockBean annotation only works when you run (a part of) your Spring Boot application.
This is why you use the #SpringBootTest annotation.
The downside is that this might require additional configuration and slows down the tests due to starting/stopping the application.
If you only need to unit test a single class with some mocks, it would be easier to write a test with Mockito only.
To do so, use the #Mock annotation and replace #SpringBootTest with #ExtendWith(MockitoExtension.class):
// Replace #SpringBootTest with #ExtendWith to enable testing with Mockito
#ExtendWith(MockitoExtension.class)
class EntityServiceTest {
// Add #InjectMocks to the class where the mocks should be injected into.
// Normally this is the class that you want to test
#InjectMocks
private EntityService service;
// Use #Mock in stead of #MockBean
#Mock
private EntityRepository repository;
// ...
}
3. Test the behaviour of your class
As I mentioned before, a mock is a dummy implementation of a class.
So that means that if you call repository.save(..), it doesn't really do anything as there's no behaviour and no database behind it.
What you actually want to test is whether the service. serviceDeleteEntity() method calls the repository. specialDeleteEntity() method with the right arguments.
This means that your second example of your test is the right way.
The only thing you don't need is the entity because your test doesn't rely on it (assuming that your service passes the id argument to the repository and returns the result of the query):
#Test
void testDelete() {
// Given
when(mockEntityRepository.specialDeleteEntity(1L)).thenReturn(1);
// When
final int result = entityService.specialDeleteEntity(1L);
// Then
assertThat(result).isEqualTo(1);
verify(mockEntityRepository).specialDeleteEntity(1L);
}
thanks to #g00glen00b for the solution.
#ExtendWith(MockitoExtension.class)
public class EntityServiceImplTest {
#InjectMocks
private EntityServiceImpl entityServiceImpl;
#Mock
private EntityRepository entityRepository;
#Test{
Entity e = new Entity();
when(entityRepository.getEntityById(1L)).thenReturn(e);
final Entity result = entityServiceImpl.getEntityById(1L);
assertEquals(e, result);
}
}
The issue here was that i had to use MockitoExtension on one hand.
Another problem was that, in my service implementation method , there was a hidden "new Timestamp(System.currentTimeMillis())" used to call the method in the repository.
So there was always a difference between the time I "promised" and the time created during the call of the service implementation.
Thank you again
I think you have to insert the mocked repository into your service in order it to be used:
entityService.setRepository(mockEntityRepository);

How to pass param to mocked class method?

I am using mockito to test mu jdk11-springboot application.
My application has class 'ClientRepository' and that has a method called 'findById' which takes a param of type UUID.
SO the method looks like :
public String findById(UUID id)
Now I mocked the class to test as :
#MockBean
private ClientRepository clientRepo;
Now I am trying to figure out how to pass the UUID param here:
Mockito.when(clientRepo.findById(UUID id))
.thenReturn(dslContext.selectFrom(CLIENT).where(CLIENT.ID.eq(UUID.fromString("3e064b19-ef76-4aea-bf82-e9d8d01daf1c"))).fetch());
Can anyone help?
You can use the following construction:
UUID expected = ...;
Mockito.when(clientRepo.findById(Mockito.eq(expected))).thenReturn(...);
This can be a good solution if the expected UUID is not the same instance that you configure in the test.
Another point to consider:
You seem to use JOOQ but have a mock bean for repository, which means that you probably test some kind of service (business logic layer).
In this case maybe you don’t need to work with database at all, just create a string and return in thenReturn part of the mocking configuration
Mockito.when(clientRepo.findById(Mockito.any(UUID.class))
.thenReturn(dslContext.selectFrom(CLIENT).where(CLIENT.ID.eq(UUID.fromString("3e064b19-ef76-4aea-bf82-e9d8d01daf1c"))).fetch());
Use Mockito.eq("your-uuid-goes-here") instead if your mock should only react to specific values.
Passing the value directly for which the mock is expected to return should also work.
Mockito.when(clientRepo.findById(<expected UUID>)
.thenReturn(dslContext.selectFrom(CLIENT).where(CLIENT.ID.eq(UUID.fromString("3e064b19-ef76-4aea-bf82-e9d8d01daf1c"))).fetch());

annotations not working when migrating to java 11

Is there anything i can do to get the #Mock and #InjectMocks annotation to work with java 11?
Not sure why this worked for you in Java7, but you clearly use #InjectMocks in a wrong way.
#InjectMocks
private ByVehicleIdRequest request = new ByVehicleIdRequest(
locale,
additionalInfo,
permissions,
switches,
vehicleId,
aftermarketDescriptions,
enhancedStringMatchingSettings);
If you use Mockito runner (or extension in JUnit5), you should not initialize the field yourself:
#InjectMocks
private ByVehicleIdRequest request;
As described in MockitoJUnitRunner documentation:
Mocks are initialized before each test method.
If you use the mock values in the field initialization of request object, you are passing not-yet-initialized values to the constructor, and hence null values you observe.
Note
The answer refers to question version 3.

EasyMock - Partial Mocking assigning the mock to specific field

In EasyMock, when using annotation #Mock
You can set the to which member of the target object the mock will be assigned, using fieldName
Example:
/*assign mock impl ONLY for myInterface2 member of tested object*/
#Mock(type= MockType.NICE, name="secondMember", fieldName = "myInterface2")
private MyInterfaceImpl myInterfaceMock; //step 1 for runner
This is very convenient as I don't need to create a special constructor to assign the internal members with mocked objects
Question:
Is there such thing for partial mocking as well?
Many Thanks!
No. #Mock can't create partial mocks (yet). You can request it as a feature request: https://github.com/easymock/easymock/issues

Mocking a Private Variable that is Assumed to Exist

How can you get a mock object in at runtime when it is not created/initialized in the class you are testing, it is not static (singleton pattern), or you don't have some sort of test constructor to hook into?
In a class that I am writing some unit testing for, I have come across a scenario I haven't encountered/solved yet. I have a JMS resource (a QueueConnectionFactory for reference, but it shouldn't matter), that is a private variable of the class I am testing. Since it has the javax.annotation.Resource annotation, at runtime it is assumed to be available. During testing, it is not, which creates the need for mocking this object.
It is not a static class and is not being used in a static way, if it was I could easily mock using the various static mocking methods I have run into. Since the resource is never created locally (in a constructor or even in a test constructor), I have no way of passing in a Mock object so that at runtime of the test, the mock is used instead of the actual object. How can I mock this Resource so that when the test executes, it will be used in place of the private #Resource object in the class I am testing?
For reference, the code is calling createConnection() on the QueueConnectionFactory which is throwing a null pointer exception since the Factory has not been initialized/mocked.
#Stateless
public class Example{
#Resource(name = "jms/exampleQCF")
private QueueConnectionFactory queueFactory;
...
public void testMe(){
Connection connection = queueFactory.createConnection();
...
}
}
After a lot more hunting around and looking at all the options Mockito/Powermock had to offer, I found the solution (which I will share in case others run into this same issue).
When you have private member variables that are never initialized (and just assumed created in other places), you can use the #InjectMocks annotation to "inject" Mocks you want into your class you are testing.
Add a variable in your test class for the class you are testing, and give it the annotation #InjectMocks (org.Mockito.InjectMocks).
Use #Mock annotations to setup the mocks you want to inject. Use the #Mock (name = "privateVariableNameHere") name property to map the Mock object to the private variable inside your class you are testing.
In either a setup function or before you call your class, initialize the mocks. The easiest way I have found is to use a "setup" method with the #Before annotation. Then inside there call MockitoAnnotations.initMocks(this); to quickly initialize anything with the #Mock annotation.
Define your Mock functionality in your test method (before calling the method you are testing).
Using the #InjectMock object, call your method you are testing... the mocks SHOULD be hooked in and working as defined in the earlier steps.
So for the example class I use above, the code to test/mock would have Connection returned as a mock which you can do whatever with. Based on the example above in my question, this is what the code would look like:
#RunWith(PowerMockRunner.class)
#PrepareForTest({/* Static Classes I am Mocking */})
public class ExampleTest {
#Mock (name = "queueFactory") //same name as private var.
QueueConnectionFactory queueFactoryMock;
#Mock
Connection connectionMock; //the object we want returned
#InjectMocks
Example exampleTester; //the class to test
#Before
public void setup(){
MockitoAnnotations.initMocks(this); // initialize all the #Mock objects
// Setup other Static Mocks
}
#Test
public void testTestMe(){
//Mock your objects like other "normally" mocked objects
PowerMockito.when(queueFactoryMock.createConnection()).thenReturn(connectionMock);
//...Mock ConnectionMock functionality...
exampleTester.testMe();
}
}
Several approaches here:
ReflectionTestUtils of Spring Testing framework: ReflectionTestUtils.setField(objectToTest, "privateFieldName", mockObjectToInject);. With this you don't introduce another dependency.
org.mockito.internal.util.reflection.FieldSetter.
PowerMock.Whitebox.setInternalState() to mock a private field.
If you need to mock internal local variable creation, use PowerMockito.whenNew(Foo.class).withNoArguments().thenReturn(foo);. Very, very useful. Cannot find other ways to do the same.
With only Mockito you cannot mock local variable creation, because when(any(Foo.class) does not work; will return null. It compiles but does not work.
References:
Mockito: Mock private field initialization

Categories

Resources