Is it possible to both mock an abstract class and inject it with mocked classes using Mockito annotations. I now have the following situation:
#Mock private MockClassA mockClassA;
#Mock private MockClassB mockClassB;
#Mock(answer = Answers.CALLS_REAL_METHODS) private AbstractClassUnderTest abstractClassUnderTest;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
Whitebox.setInternalState(abstractClassUnderTest, mockClassA);
Whitebox.setInternalState(abstractClassUnderTest, mockClassB);
}
I'd like to use something like #InjectMocks on AbstractClassUnderTest but it can't be used in combination with #Mock. The current situation, with Whitebox from Powermock, works but I'm curious if it's possible to solve it with just annotations. I couldn't find any solutions or examples.
(I know about the objections to test abstract classes and I'd personally rather test a concrete implementation and just use #InjectMocks.)
I am not aware of any way to go about this, for one clear reason: #InjectMocks is meant for non-mocked systems under test, and #Mock is meant for mocked collaborators, and Mockito is not designed for any class to fill both those roles in the same test.
Bear in mind that your #Mock(CALLS_REAL_METHODS) declaration is inherently dangerous: You're testing your AbstractClassUnderTest, but you are not running any constructors or initializing any fields. I don't think you can expect a test with this design to be realistic or robust, no matter what annotations can or cannot do for you. (Personally, I was previously in favor of real partial mocks of abstract classes as a "tool in the toolbox", but I'm coming around to thinking they're too far removed from reality to be useful.)
Were I in your position, I would create a small override implementation for testing:
#RunWith(JUnit4.class) public class AbstractClassTest {
/** Minimial AbstractClass implementation for testing. */
public static class SimpleConcreteClass extends AbstractClass {
public SimpleConcreteClass() { super("foo", "bar", 42); }
#Override public void abstractMethod1() {}
#Override public String abstractMethod2(int parameter) { return ""; }
}
#InjectMocks SimpleConcreteClass classUnderTest;
#Mock mockClassA;
#Mock mockClassB;
}
At this point, you have a simple and predictable AbstractClass implementation, which you can use even without a mocking framework if you just wanted to test that AbstractClass has the same API for extension that it did before. (This is an often-overlooked test for abstract classes.) You can even extract this, as it may be useful for other testing: Should you want to override the abstract behavior for a single test class, you can create an anonymous inner class with just a single method override, or you can set classUnderTest = spy(classUnderTest); to set up Mockito proxying and the behavior you want.
(Bear in mind that #InjectMocks and #Spy can't be used reliably together, as documented in this GitHub issue and the Google Code and mailing list threads to which it links.)
I found some trick with mocking field before initialization.
#InjectMocks
private AbstractClass abstractClass;
#Mock
private MockClass mockClass;
#Before
public void init() {
abstractClass= mock(AbstractClass.class, Answers.CALLS_REAL_METHODS);
MockitoAnnotations.initMocks(this);
}
Maybe it'll help someone.
Related
I'm having a situation in which I have certain mocking and utilities (not static methods, but certain manipulations relying on mocks).
Lets say like this,
class MyReusableClassForTesting {
#Mocked
private ClassA attribute;
// And some more #Mocked and methods that are relying on the mocks.
}
I want to use the instance of MyReusableClass in my test classes with mocks injected.
class MyTestClass {
// Need this be a real instance with mocks injected inside it
private MyReusableClassForTesting instance;
}
I tried with #Mocked, #Capturing, #Tested and #Injectable. None of them seem to work. Any idea, how can I make this work with Jmockit?
You would normally just do this:
class MyTestClass {
// Need this be a real instance with mocks injected inside it
#Tested
public MyReusableClassForTesting instance;
}
If the 'instance' constructor takes arguments, or if it has things Autowired, then add (possibly multiple) at the test-class level:
#Injectable
protected ClassA attribute;
To make sure JMockit is working, I usually add a simple test
#Test
public void testCtor() {
assertNotNull(instance);
}
JMockit will take care of creating the 'instance' based on #Tested and #Injectable. The only way this test fails is if JMockit isn't working - i.e. you forgot to add the javaagent. You generally need the javaagent in BOTH your build script (build.gradle or pom.xml) as well as when you run the test manually from your IDE.
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!
In my implementation class I have a read write lock definition as follows,
#Inject
#Named("CustomizedLock")
private ReentrantReadWriteLock rwLock;
I'm using it in a method named run() as,
rwLock.writeLock().lock();
to lock the process. But when I'm trying to test this with mockito I found ReentrantReadWriteLock is initialized. But when I'm trying to get rwLock.writeLock() it's null. Here is my test.
#Mock
private ReentrantReadWriteLock feedReadWriteLock;
#InjectMocks
private CustomModule module = mock(CustomModule.class);
/////////////////////////
#Test
public void test() {
when(module.getReadWriteLock()).thenReturn(mock(ReentrantReadWriteLock.class));
PowerMockito.doReturn(new ReentrantReadWriteLock()).when(module.getReadWriteLock());
cacheJob.run();
}
As I said rwLock.writeLock() is null but rwLock is initialized. Please explain how this happens with mockito. And what is the ideal way to do this?
You are getting mocking wrong:
#Mock
private ReentrantReadWriteLock feedReadWriteLock;
The above creates a mock that you then somehow have to get into your class under test.
But this:
#InjectMocks
private CustomModule module = mock(CustomModule.class);
is bogus. The InjectMocks annotation exists to do that "getting it into" part for you.
In other words, you should be doing something like:
#Mock
private ReentrantReadWriteLock feedReadWriteLock;
#InjectMocks
private CustomModule module = new CustomModule();
for example. In other words: you do not mock an instance of your class under test. You create a "real" instance - and then you have to "insert" the required mock objects into that real instance. There is absolutely no point in mocking the class under test. Because you want to test your code, not something that the mocking framework mocked for you.
The InjectMocks annotation tries to do that for you (using reflection and all kinds of black magic). Unfortunately when it can't do its job, it just silently fails.
In that sense, the answer is: don't just blindly use stuff. You have to fully understand each and any line of code that you write down. Thus: read this to understand the difference between these annotations.
And just for the record: please note that you also have to either use the Mockito JUnitRunner or to manually call Mockito.initMocks() in order have the annotations do their magic.
If you want to mock the class under test, you should use #Spy.
#RunWith(MockitoJUnitRunner.class)
public class CustomModuleTest {
#Mock
private ReentrantReadWriteLock feedReadWriteLock;
#Spy
#InjectMocks
private CustomModule module;
#Test
public void test() {
doReturn(feedReadWriteLock).when(module).getReadWriteLock();
cacheJob.run();
}
}
Note that the syntax is a bit different with a spy than with a mock. The spy allows your to instantiate the real object, but mock some methods as you wish.
Here, I return feedReadWriteLock, which is annoted with #Mock, this way, you may be able to change its behavior. You don't want to return a new instance like you did in your example.
valid construction:
#InjectMocks
SomeClass sc = mock(SomeClass.class);
Invalid construction:
#InjectMocks
#Mock
SomeClass sc;
I want to inject mocks to another mock. I want to use only annotation style.
Why was in Mockito forbid second construction ?
Update
example:
public class ArrTest {
private SomeClass someClass;
public List<String> foo(){
anotherMethod(); // I suppose that this method works. I want to test it separately.
//logic which I need to test
return someClass.doSmth();// I suppose that this method works. I want to test it separately.
}
public void anotherMethod(){
///...
}
}
public class SomeClass {
public List<String> doSmth(){
return null;
}
}
test:
public class ArrTestTest {
#InjectMocks
ArrTest arrTest = Mockito.mock(ArrTest.class);
#Mock
SomeClass someClass;
#Test
public void fooTest(){
Mockito.when(someClass.doSmth()).thenReturn(new ArrayList<String>());
Mockito.doNothing().when(arrTest).anotherMethod();
System.out.println(arrTest.foo());
}
}
It sounds like you're trying to do something that doesn't really make sense. You shouldn't need to inject any dependencies into your mock since mocks by definition don't have any behaviour until you define it with when(mock.someMethod()).thenAnswer() or some variation.
(except perhaps if you're using a spy(), but you've specifically said you're using a #Mock).
Maybe you could explain your use case and why you're trying to inject dependencies into a mock?
#InjectMocks specifically indicates that the annotated field will NOT contain a mock. Annotating #InjectMocks #Mock is not just unsupported—it's contradictory.
To return stubs wherever possible, use this:
#Mock(answer=Answers.RETURNS_DEEP_STUBS)
YourClass mockYourClassWithDeepStubs;
But heed the official documentation for this Answer:
WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (...), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).
Good quote I've seen one day on the web: every time a mock returns a mock a fairy dies.
A mock doesn't have any real implementation. #InjectMocks would try to find and call setters for whatever mock objects have already been created and pass them in. Mockito "knows" that this is kinda pointless on a mock, since there won't be any way to get the mock objects back out, much less do anything meaningful with them.
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