I am mocking an abstract class like below:
myAbstractClass = Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS);
the problem is that MyAbstractClass has some dependencies injected via EJB annotations and there are not setters. Is there a way to inject the dependencies?
#InjectMocks does not work with Abstract classes.
I am using junit5 for this.
What I did is instantiate the abstract class with a new abstractClass() in #BeforeEach and call the methods by super if the method is not abstract(using this because I have protected methods), after this I use ReflectionUtils.setField() to set the mocks in the abstract class and test every method and works pretty well. I leave a simple example that works.
AbstractClass
public abstract class AbstractClass {
#Autowired
private Environment environment;
protected String getProperty(String property){
return environment.getRequiredProperty(property);
}
}
AbstractClassTest
#ExtendWith(MockitoExtension.class)
class AbstractClassTest {
AbstractClass abstractClass;
#Mock
Environment environment;
#BeforeEach
void setUp() {
abstractClass = new AbstractClass() {
#Override
public String getProperty(String property) {
return super.getProperty(property);
}
};
ReflectionTestUtils.setField(abstractClass, "environment", environment);
}
#Test
void shouldReturnProperty() {
String propertyValue = "this property";
when(environment.getRequiredProperty("property")).thenReturn(propertyValue);
String property = abstractClass.getProperty("property");
assertEquals(propertyValue, property);
}
}
This is just using mockito and junit5 to test.
Remember to call ReflectionUtils after you instantiate the class with new AbstractClass() or the mocks won't be injected.
Any improve on this implementation is welcome :D.
Since you cannot instantiate an Abstract class there is nothing to test. I would recommend that you create child class (it could be a nested class inside your test class), and then run your tests that way. Then you can use the #Mock, #InjectMocks as you would normally.
You can use the Powermock library to inject mocks in myAbstractClass using Whitebox.setInternalState(myAbstractClass, mock(MockedClass.class));
Junit 4 specific solution
Abstract class that need to be tested
#Slf4j
public abstract class AdhocNotificationEmail {
#Autowired
protected CustomerNotificationRepository customerNotificationRepository;
protected abstract Map<String, String> abstractMethod(AdhocNotificationDTO adhocNotificationDTO);
public JSONObject concreteMethod(){
// some stuff that needs to be tested and common to all subclasses
}
}
Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
public class AdhocNotificationEmailTest{
#Mock
protected CustomerNotificationRepository customerNotificationRepository;
private AdhocNotificationEmail unit;
#Before
public void setUp() {
unit = new AdhocNotificationEmail() {
#Override
protected Map<String, String> abstractMethod(AdhocNotificationDTO notificationDTO) {
return null;
}
};
unit.customerNotificationRepository = customerNotificationRepository;
}
#Test
public void concreteMethod_greenPath() {
final String templateName = "NOTIFICATION_TEMPLATE";
final AdhocNotificationDTO adhocNotificationDTOStub = getAdhocNotificationDTOStub(templateName);
final CustomerNotification customerNotificationStub = getCustomerNotificationStub(templateName);
when(customerNotificationRepository.findByIdAndTemplateName(id, templateName)).thenReturn(customerNotificationStub);
final JSONObject response = unit.concreteMethod(adhocNotificationDTOStub);
assertNotNull(response);
}
Personally, what I like to do is to extend the abstract with an anonymous class declared in the #Before (or #BeforeEach in junit.jupiter). This way I can achieve the following:
Don't mock the class that I want to test (like you are doing with Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS)), since that is kind of an anti-pattern. You only want to Mock the dependencies of the class you are testing;
You can provide Mocks for the dependencies of the abstract class, and that are called by non-abstract methods;
You can test non-abstract methods that call abstract methods
Example:
class TestAbstractClass {
#Mock
private ServiceDependency dependency;
private AbstractClass abstractClass;
#BeforeEach
void setUp() {
abstractClass= new AbstractClass (dependency) { };
}
#Test
void test(){
Mockito.when(dependency.someMethod()).thenReturn(something);
var result = abstractclass.someNonAbstractMethod();
// assertions
}
}
Good practice is to write unit tests for all possible classes which inherits from this abstract class. Because in theory it is possible situation. This dependency should be mocked and you should forget about mocking dependencies of this EJB component.
Maybe some code snippets would help to clarify what you try to achieve here.
Related
I think there is either a basic misunderstanding on my part with how when works, or more specifically how Mockito is working.
I have a service class that has a utility class injected via constructor. The utility class has some other dependencies autowired by constructor as well.
A service class method calls several methods in the utility class. The test uses when/thenReturn statements on the called utility methods. When I make a call on the service method, I get an NPE on a utility method called with a null parameter. But I expected parameters set in the when clause to be set. Code below:
#Service
public class ServiceClass {
private Utility utility;
public ServiceClass(Utility utility) {
this.utility = utility;
}
public serviceMethod(MyDocument myDocument, List<Attachment> attachments) {
SomeType variable1;
OtherType variable2;
List<String> stringList;
long time;
time = utility.method1(variable1, variable2);
stringList = utility.method2(myDocument, attachments.get(0));
...
}
#Service
public class Utility {
private Dependency1 depend1;
private Dependency2 depend2;
public Utility(Dependency1 depend1, Dependency2 depend2) {
this.depend1 = depend1;
this.depend2 = depend2;
}
public long method1(SomeType var1, OtherType var2) {
....
}
public List<String> method2(MyDocument myDoc, Attachment attach) {
....
}
Now the test code looks as follows:
public TestClass {
private ServiceClass serviceClass;
#Mock
private Depend1 depend1;
#Mock
private Depend2 depend2;
#InjectMocks
private Utility utility;
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Before
public void setup() {
serviceClass = new ServiceClass(utility);
}
#Test
public testServiceMethod() {
long time = System.currentTimeMillis();
MyDocument doc = new MyDocument();
List<Attachments> attachments = Arrays.asList(new Attachment(...), new Attachment(...));
SomeType some = new SomeType();
OtherType other = new OtherType();
when(utility.method1(some, other)).thenReturn(time);
when(utility.method2(doc, attachments.get(0)).thenReturn(Arrays.asList(new String("stg 1"), new String("stg 2"));
String resp = serviceClass.serviceMethod(doc, attachments);
assertEquals("service completed", resp);
}
}
But when utility.method2 is called, myDocument shows as null. I was expecting that it would be an instance of MyDocument.
Do I have something misconfigured? Am I missing a concept here? All help appreciated!
Thanks.
UPDATE
Corrected the arguments to the serviceMethod.
The ServiceClass is the class you are testing, so you should anotate with #Mock only the dependencies of this class in your test, in your case the utility attribute, remove Depend1 and Depend1 declarations. The setup method is not necessary, you can anotate serviceClass in your test with #InjectMocks instead, it take cares of the injections automatically. And finally, your TestClass need the #RunWith(MockitoJunitRunner.class) to make everything work if it's not there.
#RunWith(MockitoJunitRunner.class)
public class TestClass{
#InjectMocks
private ServiceClass serviceClass;
#Mock
private Utility utility;
}
This is a basic definition for your TestClass, the test itself looks correct, but can be improved to use ArgumentMatchers on the "when" clause and add a verify clause using ArgumentCaptor to validate the parameters.
My FixValueConceptIntegration class has a constructor and it looks like this:
private ReferenceConceptHelper referenceConceptHelper;
private ConceptClientFacade conceptClientExternalFacade;
public FixValueConceptIntegration()
{
referenceConceptHelper = JournalSingletonFactory.getInstance().getSingletonInstance(ReferenceConceptHelper.class);
conceptClientExternalFacade = JournalSingletonFactory.getInstance().getSingletonInstance(ConceptClientFacade.class);
}
So now I'm going to test it using Mockito.
If we have a constructor like
public FixValueConceptIntegration(ReferenceConceptHelper referenceConceptHelper, ConceptClientFacade conceptClientExternalFacade)
{
this.referenceConceptHelper = referenceConceptHelper
this.conceptClientExternalFacade = conceptClientExternalFacade
}
I know it is easy to initialize when we are going to testing the class. Because we can just mock the ReferenceConceptHelper and ConceptClientFacade classes.
Then we can use it at the #BeforeMethod like this:
#BeforeMethod
public void beforeMethod()
{
MockitoAnnotations.initMocks(this);
fixValueConceptIntegration = new FixValueConceptIntegration(referenceConceptHelper, conceptClientExternalFacade);
}
Then all the dependencies will inject to the constructor and no worries.
So here the problem is I can't figure out how to inject these dependencies (by mocking) to the above testable class.
Just use the mock (org.mockito.Mockito.mock) method for the class and the when method to mock the method calls:
#Test
public void yourTest() {
ReferenceConceptHelper referenceConceptHelper = mock(ReferenceConceptHelper .class);
when(referenceConceptHelper.someMethod(any()).thenReturn("hello");
ConceptClientFacade conceptClientExternalFacade = mock(ConceptClientExternalFacade.class);
when(conceptClientExternalFacade.someMethod(any()).thenReturn("world");
FixValueConceptIntegration integration = new FixValueConceptIntegration(referenceConceptHelper, conceptClientExternalFacade);
assertEquals("hello world", integration.methodThatYouWouldLikeToTest());
}
In this case, you do not need to use the #BeforeMethod or call MockitoAnnotations.initMocks(this);. For unit tests, the initMocks are only useful if you do not have access directly to the class injected (typically when you are using field injection).
But if you would like to use the annotations (I personally don't like), you can do something like that:
#InjectMocks
private FixValueConceptIntegration integration;
#Mock
private ReferenceConceptHelper referenceConceptHelper;
#Mock
private ConceptClientFacade conceptClientFacade;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
when(referenceConceptHelper.someMethod(any()).thenReturn("hello");
when(conceptClientExternalFacade.someMethod(any()).thenReturn("world");
}
#Test
public void yourTest() {
assertEquals("hello world", integration.methodThatYouWouldLikeToTest());
}
I extended my test class with TestNGBase which extends PowerMockTestCase.
And then add registerMockSingleton method to the TestNGBase class like this;
protected <E, I extends E> void registerMockSingleton(Class<E> typeInterface, I mock)
{
delegate.registerMockSingleton(typeInterface, mock);
}
Then inject mock dependencies to the constructor like this way;
#Override
public void performSetup() {
MockitoAnnotations.initMocks(this);
registerMockSingleton(ReferenceConceptHelper.class,mockReferenceConceptHelper);
registerMockSingleton(ConceptClientFacade.class,mockConceptClientExternalFacade);
fixValueConceptIntegration = new FixValueConceptIntegration();
}
#Override
protected void performTearDown() throws Exception
{
fixValueConceptIntegration = null;
}
All solved!!!
(My testable class constructor doesn't inject dependencies to with constructor
arguments.Thats why I solved my problem like this)
I'm new to JMockIt and am trying to figure out/understand how to use #Injectable for a top-level MockUp class that I have already defined.
For example:
// JUnit Test Class
public class RepositoryTest {
#Tested private Repository repository;
#Injectable private ResultsAPIWrapper resultsApiWrapper;
#Test
public void testRepo(){
new ResultsApiWrapper();
assertThat(repository.doSomething(), is("done" ) );
}
}
// Class under test
public class Repository{
#Autowired private ResultsAPIWrapper resultsApiWrapper;
public String doSomething(){
return resultsApiWrapper.load();
}
}
// Mocked implementation of the ResultsAPIWrapper that I want injected into Repository
public class ResultsApiWrapperMock extends MockUp<ResultsAPIWrapper>{
#Mock
public String load(){
return "done";
}
}
If I try the above, I get an error
java.lang.IllegalArgumentException: Class already mocked: com.fw.wrappers.ResultsAPIWrapper
at com.fw.wrappers.mock.ResultsApiWrapperMock.<init>(ResultsApiWrapperMock.java:12)
at com.fw.repository.RepositoryTest.testRepo(RepositoryTest.java:38)
But If I remove the new ResultsApiWrapper() then I do not know how to specify which is the class I want to use as my mocked implementation for the autowire.
Am I misunderstanding how to do this? How can I specify that I want JMockit to autowire using my MockUp<> implementation?
I'm new too but I think something like this would work in your case...
This will mock the ResultsAPIWrapper() and not initialize any static variables and create a specific mock for load():
new MockUp<ResultsAPIWrapper>() {
#Mock
void $clinit() {
//disable static initialization
}
#Mock
public String load() {
return "done";
}
};
I have written a testclass for a baseclass that uses a factory. I #Mocked the factory to return a Mock object.
It looks sort of like this.
class BaseClass{
SomeFactory factory;
public BaseClass(SomeFactory factory){
this.factory=factory;
}
public void parse(){
factory.createSomething();
}
}
Now my (working) testclass looks like this.
#RunWith(MockitoJUnitRunner.class)
public class BaseTest {
#Mock
SomeFactory factory;
#Mock
SomeCreation crateation;
BaseClass subject;
#Before
public void setUp(){
when(factory.createSomething()).thenReturn(someCreation);
subject = new BaseClass(factory);
}
#Test
testParse(){
subject.Parse();
verify(factory).createSomething();
}
}
This all works fine, but now i have extended BaseClass (lets call it SubClass) and added some functionality. My SubClass also uses an extended factory (SubFactory). So i also want to extend BaseTest and run the same tests, because it does the exact same thing and something extra.
so i overrode the setUp() like so:
class SubTest extends BaseTest{
#Mock
SubFactory subFactory;
#Mock
Something something;
#Override
public void setUp(){
when(subFactory.createSomething()).thenReturn(
factory = subFactory;
subject = new SubClass(subFactory);
}
}
This however doesn't work, because in the baseclass it throws an UnfinishedVerificationException saying:
Missing method call for verify(mock) here:
pointing to the verify in the BaseClass.
Any ideas on how to structure my test-cases that allows me to test the SubClass with the same tests as BaseClass?
Thank you,
Do not extend test cases! Even though there will be (lots of) duplicated code, it's easier to be read and followed.
So, the subclass test should not extend the BaseClass test, but rather re-use the tests on the functionality, which will not be overwritten in the subclass. Also, a getter for the factory would be needed to better customize the behavior on the mocked factory:
class SubClass extends BaseClass {
SubClass(SubFactory factory) {
super(factory);
}
SubFactory getFactory() { return factory; }
public void parse() {
getFactory().createSomething();
}
}
class SubTest {
#Mock
SubFactory subFactory;
#Mock
Something something;
#Mock
SubCreation someSubCreation;
SubClass subject = new SubClass(subFactory);
public void setUp() {
when(subFactory.createSomething()).thenReturn(someSubCreation);
}
}
I have test class inheritance issue: putting #Mocked and #Injectable fields to common abstract test parent class breaks auto-injection of mocked instances to #Tested class.
I use JMockit 1.5.
Here's an example:
public MyService {
private MyStorage storage;
public void myMethod(String entityId){
storage.getEntity(entityId);
// ...
}
}
public abstract class AbstractTestBase {
#Injectable
protected MyStorage storage;
}
public class MyTest extends AbstractTestBase {
#Tested
private MyService tested;
#Test
public void test_myMethod(){
new Expectations() {
{
storage.getEntity("1");
result = "foobar";
}
tested.myMethod("1"); // <-- here I have NPE
// as storage is not injected properly.
// ...
};
}
}
If I move #Injectable MyStorage storage to MyTest class everything gets injected correctly to #Tested class.
How can I allow auto-injections with common parent for test classes?
I found out the issue is solved in the most up to date release 1.11.