Reputation: 2718
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.
Upvotes: 10
Views: 19887
Reputation: 21
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:
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;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
}
}
Upvotes: 0
Reputation: 2590
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);
}
Upvotes: 0
Reputation: 73
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.
Upvotes: 5
Reputation: 197
You can use the Powermock library to inject mocks in myAbstractClass
using Whitebox.setInternalState(myAbstractClass, mock(MockedClass.class))
;
Upvotes: 1
Reputation: 127
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.
Upvotes: -1
Reputation: 2376
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.
Upvotes: 0