Reflection in multiple wrapped objects - java

In a JUnit test, I want to change the hibernate template in a Spring DAO. This DAO is
annotated with #Transactional so it gets wrapped during runtime and
spyed upon by Mockitos spy()- method. So the DAO will be wrapped a second time by that spy.
So the DAO now has two wrapping objects: One from #Transactional, one from the spy. Due to the fact that it's not known which of those wrapper is created first, I can't set the hibernate template in the DAO via reflection.
How can I set the template in the doubled-wrapped DAO?
[Edit]
Some Source:
/**
* This class gets wrapped by a proxy object because of #Transactional.
*/
#Transactional
public class MyDao implements SomeDaoInterface { ... }
In a test class:
public class MyTestClass {
#Autowired
private MyDao myDao;
#Test
public void myTestMethod() throws Exception {
final MyDao daoSpy = spy(myDao); // Dao gets wrapped with second wrapper
final Field field = MyDao.class.getDeclaredField("template");
field.setAccessible(true);
field.set(daoSpy, mySpecialMockedTemplate); // ERROR: want to inject the template but
// dont know in which wrapper
}
}

Call the setter method instead of accessing the field.

It seems you reflection code is wrong. Use this statement instead :
field.set(daoSpy, mySpecialMockedTemplate);
However looking at your test code, it seems you are using Spring to create the MyDao instance. It seems kind of weird to use reflection to set the template, why not configure it in Spring ?
Or even use an actual setter ? Or make the field protected, so only the unit test can access it.
EDIT : About injection, you could create the DAO instance in your test and have your specialMockedTemplate injected by Mockito. You could writ something like :
#RunWith(MockitoJUnitRunner.class)
public class MyTestClass {
#InjectMocks private MyDao dao;
#Mock SpecialTemplate specialTemplate;
#Test void dao_should_call_the_template_with_parameter_A_and_B() {
// given
// when
dao.someCall("A", "B");
// then
verify(specialTemplate).someCallWith("A", "B");
}
}
A few warnings though, avoid partial mocking if possible (using spies). Avoid moking types you don't own, you should read this blog post entry why this is a bad idea.

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

Calling real method using Mockito

I am using JDK 11 and spring boot.
I am implementing a rest API and have 3 layers:
controller
service layer
data access layer
I had classes against interfaces at the data-access-layer and did not have any interface at the service layer.
I wrote integration tests using MockMvc, Mockito, etc to exercise the whole path for each point, exposed by the controller. This was not a problem until I tried to introduce the interface at the service layer.
Initially, I mocked only repositories/Daos. So the class structure looked like:
public interface ClientRepo{
......
}
public class ClientRepoImpl implements ClientRepo{
......
}
Mocked the returned data as:
#MockBean
private ClientRepo client;
....
Mockito.when(client.isExistFkUnitId(Mockito.any(UUID.class))).thenReturn(false);
Everything was fine so far.
Now I have introduced interface at the service layer as :
public interface ClientService{
......
}
public class ClientServiceImpl implements ClientService{
......
}
And tried ( Trying to call actual service method):
#MockBean
private ClientService clientService;
....
Mockito.when(clientService.isExistFkUnitId(Mockito.any())).thenCallRealMethod();
But getting nothing but null all the time.
Is there a way to make the real method call keeping the interface?
I think you want to use #Spy annotation instead of #Mock annotation on the field where you want to call the real method. I don't happen to have an example to verify this though.
https://javadoc.io/doc/org.mockito/mockito-core/2.21.0/org/mockito/Spy.html
Then you can do doCallRealMethod().when(clientService.isExistFkUnitId(Mockito.any())).
Because with a spy object you call doReturn/when instead of when/doReturn.
https://javadoc.io/doc/org.mockito/mockito-core/2.21.0/org/mockito/Mockito.html#do_family_methods_stubs
Well, there is no "real" method to call. (Ignoring the fact that default methods in interfaces are a thing nowadays)
Generally, unit tests should be written for the target class in an isolated fashion. Like this, you are always "testing" the "isExistFkUnitId" method as well.
You could set the mock up for specific values:
Mockito.when(clientService.isExistFkUnitId("valueA").thenReturn("answerA");
Mockito.when(clientService.isExistFkUnitId("valueB").thenReturn("answerB");
Anyways... to respond to your actual question:
If possible, you can instantiate the implementation in a way that the desired method is working and call it through the mock:
ClientServiceImpl clientServiceImpl = new ClientServiceImpl(...);
// spaghetti code only for demonstration purposes ;)
Mockito.when(clientService.isExistFkUnitId(Mockito.any())).then(i -> clientServiceImpl.isExistFkUnitId((String) i.getArguments()[0]));
POC test:
#Test
public void testit() {
Myclass myclass = new Myclass();
Myinterface mock = Mockito.mock(Myinterface.class);
Mockito.when(mock.myMethod(Mockito.any())).then(i -> myclass.myMethod((String) i.getArguments()[0]));
assertThat(mock.myMethod(" works")).isEqualTo("yeehaa works");
}
public interface Myinterface {
String myMethod(String params);
}
public static class Myclass implements Myinterface {
#Override
public String myMethod(String params) {
return "yeehaa" + params;
}
}
Not exactly a beautiful solution, but if there is no way around it, it should work.
As you are mocking an interface Mockito doesn't know which implementation are you referring. The only way will be to use the Class.
I was having the same problem. My problem was due to the ClientService having dependencies that were not mocked when I set up the tests in this format. So ClientService had a mock, but if I tried clientService.productService.get() or something of that nature the dependant productService was always null. I solved this using testing reflection:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps!

How to mock a class created in constructor?

Imagine I have a bean JdbcTemplate, and in only once class I need NamedParameterJdbcTemplate explicit. It is created inside the constructor.
Question: how can I mock it during junit tests?
#Service
public class QueryService {
private final NamedParameterJdbcTemplate namedJdbc;
public BookingExportService(JdbcTemplate jdbc) {
this.namedJdbc = new NamedParameterJdbcTemplate(jdbc);
}
public void sql() {
namedJdbc.query(sql1, mapSqlParameterSource, resultSetExtractor);
namedJdbc.query(sql2, mapSqlParameterSource, resultSetExtractor);
}
}
Defining the mock is difficult here, as I have to mock the underlying JdbcTemplate calls. This could work as follows:
#MockBean
private JdbcTemplate jdbc;
#Test
public void testQuery() {
when(jdbc.query(any(PreparedStatementCreator.class), any(ResultSetExtractor.class))).thenReturn(..);
}
BUT: if I have different sql queries that should produce different results (like above sql1 and sql2), then I'm out of luck and cannot differ the sql during the mock.
What are my chances?
Here:
this.namedJdbc = new NamedParameterJdbcTemplate(jdbc);
That simply makes your code hard to test. Basically you are bypassing dependency injection here, and well, that means: you don't have any control over that field content.
Three options:
turn to a mocking framework like PowerMock(ito) or JMockit that allow you to control calls to new() (not recommended)
change your code, for example using constructor telescoping ... so that you can simply pass in an instance of that class (which can then be mocked with "normal" frameworks like Mockito easily)
as you are already using a framework that has its own ideas of dependency injection (#Autowired for example): step back and do things like that framework implies it to be done.

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

Using JMockit to mock autowired interface implementations

We are writing JUnit tests for a class that uses Spring autowiring to inject a dependency which is some instance of an interface. Since the class under test never explicitly instantiates the dependency or has it passed in a constructor, it appears that JMockit doesn't feel obligated to instantiate it either.
Up until now we have been using SpringRunner to have Spring load mock dependencies for us, which works. Two things we don't like about this are 1) the Spring framework has to be loaded and initialized each time running the tests which is not exactly speedy, and 2) we are forced to explicitly create all mock dependencies as real classes, something which JMockit helps eliminate.
Here's a simplified example of what we're testing:
public class UnitUnderTest {
#Autowired
ISomeInterface someInterface;
public void callInterfaceMethod() {
System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
someInterface.doSomething();
}
}
So, the question is, is there a way to have JMockit create a mock someInterface?
JMockit will always instantiate a mocked interface (except in the case of a final mock field), but that only occurs in test code. It will not automatically inject the instance into code under test.
You would have to manually inject the mock instance. For example:
public class SomeTest
{
#Autowired UnitUnderTest unitUnderTest;
#Mocked ISomeInterface theMock; // created and assigned automatically
#Test
public void testSomeMethod()
{
Deencapsulation.setField(unitUnderTest, theMock);
//proceed with unit test here
}
}
mockit.Deencapsulation is a Reflection-based utility class that lets you invoke private methods, get/set fields, etc.
You can use org.springframework.test.util.ReflectionTestUtils to explicitly inject your mocked ISomeInterface in your test case.
See documentation
With the hints kindly provided above, here's what I found most useful as someone pretty new to JMockit: JMockit provides the Deencapsulation class to allow you to set the values of private dependent fields (no need to drag the Spring libraries in), and the MockUp class that allows you to explicitly create an implementation of an interface and mock one or more methods of the interface. Here's how I ended up solving this particular case:
#Before
public void setUp() {
IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {
#Mock
MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {
MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
return marketMakerDcl;
}
}.getMockInstance();
setField( unitUnderTest, theMock );
}
Thanks everyone for the help.
For those people who met
java.lang.IllegalStateException: Missing #Injectable for field ***
or
java.lang.IllegalStateException: Missing #Tested class for field ***
error when using jmockit to mock #autowired field in spring ( or spring boot) framework, I did below two steps to avoid above errors:
use #Tested(fullyInitialized=true) instead of #Tested
https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ
revert jmockit's version back to 1.18 or previous ones
https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM
If you have a #Qualifier annotation for the interface, you need to name your #Injectable field exactly as it is named in qualifier.
Here is quote from JMockit doc:
Custom names specified in field annotations from Java EE (#Resource(name), #Named) or the Spring framework (#Qualifier) are used when looking for a matching #Injectable or #Tested value. When such a name contains a - (dash) or . (dot) character, the corresponding camel-cased name is used instead.
For example:
#Component
public class AClass {
#Autowired
private Bean1 bean1;
#Autowired
#Qualifier("my-dashed-name")
private AmqpTemplate rpcTemplate;
}
Unit test class:
public class AClassTest {
#Injectable
private Bean1 bean1;
#Injectable
private AmqpTemplate myDashedName;
#Tested
private AClass aClass = new AClass();
}
Also there is no need to use setFiled for each #Autowired bean, all fields injects automatically when #Tested class instantiated. Tested on JMockit ver. 1.30

Categories

Resources