Mocking subclass items with mockito in java - java

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

Related

Mockito, object is not mocked

Here is the simple class I test. It looks strange but I have oversimplified it for a reason.
public class DishService {
DaoFactory daoFactory = DaoFactory.getInstance();
public void exampleMethod() {
System.out.println(daoFactory);
DishDao dishDao = daoFactory.createDishDao();
System.out.println(dishDao.findById(1));
}
}
And here is the DaoFactory class
public abstract class DaoFactory {
private static DaoFactory daoFactory;
public abstract UserDao createUserDao();
public abstract DishDao createDishDao();
public abstract OrderDao createOrderDao();
public static DaoFactory getInstance() {
if (daoFactory == null) {
synchronized (DaoFactory.class) {
daoFactory = new JDBCDaoFactory();
}
}
return daoFactory;
}
}
And here is my test class
#ExtendWith(MockitoExtension.class)
class DishServiceTest {
#Spy
DishService dishService;
#Mock
DishDao dishDao;
#Mock
DaoFactory daoFactory;
#Test
void example() {
MockedStatic<DaoFactory> daoFactoryDummy = Mockito.mockStatic(DaoFactory.class);
daoFactoryDummy.when(DaoFactory::getInstance).thenReturn(daoFactory);
Mockito.when(daoFactory.createDishDao()).thenReturn(dishDao);
when(dishDao.findById(1)).thenReturn(new Dish());
dishService.exampleMethod();
}
The problem is that daoFactory simply is not mocked. As you can see, I return new Dish ( default constructor ), so System.out.println() should show an empty object but it connects to DB and fetch object from real daoFactory.
And here is what I see in console
Dish{name='dadawd', description='wdadwad2', category=DRINKS, price=23131.00, imageFileName='FIRSTSnacksAsDaypart_1.jpg', orderItems=null}
Unnecessary stubbings detected.
-> at service.DishServiceTest.example(DishServiceTest.java:35)
-> at service.DishServiceTest.example(DishServiceTest.java:36)
35 and 36 lines of code you can see at the screen.
At which point do you initialize/create dishService object? In the #BeforeTest?
Since you have a field daoFactory on dishService object, DaoFactory.getInstance() gets called before you mock its methods, as it is called when you create dishService object.
So you need to create dishService object after you have mocked the Factory static methods, inside #Test method.
Or another way would be to use DaoFactory.getInstance() directly in the dishService.exampleMethod().
In both cases: first mock static method, then call it.
Also don't forget to close static mock, or even better: use it inside try-with-resources construct.

Mockito when isn't replacing original method behaviour

I got 2 modules User and Email, both of them have 1 entry point which is a facade, rest is package scoped. The configuration is done in 2 classes
#Configuration
class UserConfiguration {
#Bean
UserFacade userFacade(UserRepository repository, EmailFacade emailFacade) {
return new UserFacade(repository, emailFacade);
}
}
#Configuration
class EmailConfiguration {
#Bean
EmailFacade emailFacade(EmailSender emailSender) {
return new EmailFacade(emailSender);
}
}
Now, I want to write tests that don't require Spring to start. I implemented a simple InMemoryRepository to make this happen
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade = new EmailFacade(new FakeEmailSender());
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I need some fake objects to instantiate EmailFacade so I wrote fake implementation
public class FakeEmailSender implements EmailSender {
#Override
public void sendEmail(EmailMessage emailMessage) throws RuntimeException {
}
}
In that scenario, I'm testing User domain, so I want to mock Email anyways.
I wrote a test to check if it works
#Test
public void shouldReturnSendingFailed() {
Mockito.when(emailFacade.sendUserVerificationEmail(Mockito.any())).thenReturn(Either.left(EmailError.SENDING_FAILED));
assertThat(userFacade.registerNewUser(RegisterUserDto.builder()
.username(USERNAME_4)
.email(VALID_EMAIL)
.password(VALID_PASSWORD).build()).getLeft(), is(EmailError.SENDING_FAILED));
}
But it isn't... after running this test I got
java.util.NoSuchElementException: getLeft() on Right
edit#
regiserNewUser() method
Either<DomainError, SuccessMessage> register(RegisterUserDto registerUserDto) {
if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
return Either.left(UserError.USERNAME_ALREADY_EXISTS);
var userCreationResult = User.createUser(registerUserDto);
var savedUser = userCreationResult.map(this::saveUser);
var emailDto = savedUser.map(this::createVerificationEmail);
return emailDto.isRight() ? emailFacade.sendUserVerificationEmail(emailDto.get())
: Either.left(emailDto.getLeft());
}
Edit2#
With following test configuration
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I got nullpointer here, last line of registerNewUser().
Try running this code
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
private UserFacade userFacade;
#Before
public void setUp() {
userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
}
}
There are a few issues with your code:
You initialize your mocks twice. You don’t need to call initMocks in the setUp method if you are using Mockito runner
You are trying to inject mocks to already initialized object. But the field you are trying to inject is also passed to the constructor. Please read #InjectMocks doc, to check the strategies used to inject the mocks:
constructor (not used here, already initialized object)
setter (do you have one?)
field (is it not final)
There are details to each strategy (see my questions above). If no staregy is matched, Mockito will fail silently. The fact that you are passing an object in constructor, and rely on setter or field injection afterwards makes this code unnecesarily complex.

Inject mocks to Abstract class using mockito

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.

How can I mock methods of #InjectMocks class?

For example I have handler:
#Component
public class MyHandler {
#AutoWired
private MyDependency myDependency;
public int someMethod() {
...
return anotherMethod();
}
public int anotherMethod() {...}
}
to testing it I want to write something like this:
#RunWith(MockitoJUnitRunner.class}
class MyHandlerTest {
#InjectMocks
private MyHandler myHandler;
#Mock
private MyDependency myDependency;
#Test
public void testSomeMethod() {
when(myHandler.anotherMethod()).thenReturn(1);
assertEquals(myHandler.someMethod() == 1);
}
}
But it actually calls anotherMethod() whenever I try to mock it. What should I do with myHandler to mock its methods?
First of all the reason for mocking MyHandler methods can be the following: we already test anotherMethod() and it has complex logic, so why do we need to test it again (like a part of someMethod()) if we can just verify that it's calling?
We can do it through:
#RunWith(MockitoJUnitRunner.class)
class MyHandlerTest {
#Spy
#InjectMocks
private MyHandler myHandler;
#Mock
private MyDependency myDependency;
#Test
public void testSomeMethod() {
doReturn(1).when(myHandler).anotherMethod();
assertEquals(myHandler.someMethod() == 1);
verify(myHandler, times(1)).anotherMethod();
}
}
Note: in case of 'spying' object we need to use doReturn instead of thenReturn(little explanation is here)
All answers above are really good and may be useful so make sure you study and understand these principes first before continue reading my post.
In my scenario none of advices above did work. I will post what helped me after a pretty long debugging.
If you want to call methods from tested class, the #Spy annotation is needed alongside #InjectMocks (or Mockito.spy(XXX) call or course)
The interesting part is, the order of these annotations does matter!
The #Spy annotation must precede #InjectMocks annotation.
Will not work
...
#InjectMocks
#Spy
private TestedObject instance
...
Will work
...
#Spy
#InjectMocks
private TestedObject instance
...
In your code, you are not testing MyHandler at all. You don't want to mock what you are testing, you want to call its actual methods. If MyHandler has dependencies, you mock them.
Something like this:
public interface MyDependency {
public int otherMethod();
}
public class MyHandler {
#AutoWired
private MyDependency myDependency;
public void someMethod() {
myDependency.otherMethod();
}
}
And in test:
private MyDependency mockDependency;
private MyHandler realHandler;
#Before
public void setup() {
mockDependency = Mockito.mock(MyDependency.class);
realHandler = new MyHandler();
realhandler.setDependency(mockDependency); //but you might Springify this
}
#Test
public void testSomeMethod() {
//specify behaviour of mock
when(mockDependency.otherMethod()).thenReturn(1);
//really call the method under test
realHandler.someMethod();
}
The point is to really call the method under test, but mock any dependencies they may have (e.g. calling method of other classes)
If those other classes are part of your application, then they'd have their own unit tests.
NOTE the above code could be shortened with more annotations, but I wanted to make it more explicit for the sake of explanation (and also I can't remember what the annotations are :) )

Mocked method is never getting called

I'm trying to write a test case for a java class. I've mocked a method using Mockito, but the mock is never being used. Why?
Below is the structure of my java class:
class A {
#Autowired
private ClassB classB;
publiv void methodOne() {
methodTwo();
}
private void methodTwo() {
...
int returnedValue = classB.someMethod();
...
}
}
The test class is given below:
class ATest {
#Mock
private ClassB classB;
#InjectMocks
#Autowired
ClassA classA;
#Before
public void setupTest() {
MockitoAnnotations.initMocks(this);
}
#Test(expected = SomeException.class)
public void testMethodOne() {
when(classB.someMethod()).thenReturn(29);
classA.methodOne();
}
}
The test class is extended from another which has the #RunWith(SpringJUnit4ClassRunner.class) annotation.
I've gone through the existing questions but have been unable to find an answer. If there is any question/answer that may help me, please point me to the same.
Thanks in advance!
You could try to remove #Autowired in your test class ATest.
#InjectMocks should be sufficient as it creates your test object and injects your mock.
Depending on your JUnit runner #Autowired might perform a second injection after #InjectMocks which overrides your mocked method setup.
First, a few notes on your test:
- the test should be public
- class A should be public
- there is no need for #Autowire in the test
- there is no need to call initMocks
Then, your test isn't actually testing if the mock is being used or not. Let's change these method so that they now return the value, so that we can test if they work or not:
public class ClassA {
#Autowired
private ClassB classB;
public int methodOne() {
return methodTwo();
}
private int methodTwo() {
return classB.someMethod();
}
}
public class ClassB {
public int someMethod() {
return 0;
}
}
We can now test:
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Mock
private ClassB classB;
#InjectMocks
ClassA classA;
#Test
public void testMethodOne() {
when(classB.someMethod()).thenReturn(29);
final int result = classA.methodOne();
assertEquals(29, result);
}
}
And the test passes - meaning the mock is being used correctly.
Hope that helps.
What you're missing is that annotations are just declarations. The #Mock and #InjectMocks annotations don't do anything. They are only markers that can be seen by the test runner to perform specific runner-specific behavior. You need a #RunWith annotation to specify a test runner, but you need a specific test runner for specific behavior. If you're using the SpringJUnit4ClassRunner, then none of the Mockito annotations will be used. In any case, I wouldn't want to use the SpringJUnit4ClassRunner for unit tests, as you should just be verifying business logic, not wiring.
Change your test class to use #RunWith(MockitoJUnitRunner.class) and remove the base class reference.

Categories

Resources