I have a service that extends abstract class in my spring-boot project
public class TestService extends AbstractTest1Service {
// some methods
}
public abstract class AbstractTest1Service extends AbstractTest2Service {
public String doSomething() {
return writeText();
}
}
public abstract class AbstractTest2Service {
String writeText() {
return "text";
}
}
Is there any way to mock writeText() method when i want to test TestService:
Your TestService class:
public class TestService extends AbstractTest1Service {
AbstractTest2Service abstractTest2Service;
public String doSomething() {
abstractTest2Service.writeText();
System.out.println("Passed!");
return "All Checked";
}
}
Your TestServiceTest class should be like:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
#InjectMocks
TestService testService;
#Mock
AbstractTest2Service abstractTest2Service;
#Test
public void testService(){
Assert.assertEquals("All Checked",testService.doSomething());
}
}
Note: Do not use #Spy instead of #Mock , Spy will try to check the actual implementation of the abstract class methods which is not
there , so your test will be ignored. Just use #Mock.
Using #Spy will give error:
Cannot instantiate a #Spy for 'abstractTest2Service' field. You
haven't provided the instance for spying at field declaration so I
tried to construct the instance. However, I failed because: the type
'AbstractTest2Service is an abstract class.
Related
I am using JUnit5.
I have following scenario:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class SomeClassTest {
// needs to be mocked but not injected
#Mock
private EntityManager entityManager;
// needs to be mocked and injected
#Mock
private SomeDao someDao;
#InjectMocks
private SomeClass someClass = new someClass(entityManager);
public class SomeClass{
EntityManager entityManager;
public SomeClass(EntityManager entityManager) {
if (entityManager == null) {
throw new NullpointerException("Manager is null");
} else {
this.entityManager = entityManager;
}
The problem is:
mocked EntityManager object is needed to create the class I want to test
must inject SomeDao which is needed in SomeClass
I get a nullpointer because the mocked object appears not to be created when I give it as argument to the constructor
Has anybody an idea how to solve this? I could create a no-arg constructor but that would give me a constructor which I only need for testing which isn't that 'clean'.
You should add your SomeDao someDao as an arg to SomeClass constructor and then you will be able to manipulate it via tests using #Mock
Looking at this answer (https://stackoverflow.com/a/67081911/11431537) Mockito is using 3 ways to inject a mock.
The first one is injecting it in the constructor if there is a constructor which takes the argument.
Therefore, I changed my constructor as #nesmeyana mentioned:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.persistence.EntityManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
#ExtendWith(MockitoExtension.class)
public class SomeClassTest {
#Mock
private EntityManager mockEntityManager;
#Mock
private SomeDao mockSomeDao;
#InjectMocks
private SomeClass someClass;
#Test
public void test() {
when(mockSomeDao.duck()).thenReturn(10);
assertEquals(10, someClass.bla());
}
}
public class SomeClass{
EntityManager entityManager;
SomeDao someDao;
public SomeClass(EntityManager entityManager, SomeDao someDao) {
if (entityManager == null) {
throw new NullpointerException("Manager is null");
} else {
this.entityManager = entityManager;
this.someDao = someDao;
}
public class SomeDao{
int x = 5;
public SomeDao(int x) {
this.x = x;
}
public int duck() {
return x;
}
}
But since I had to change my code to be able to test my classes, I have to rethink my code writing in general!
I and everybody who reads this, should avoid hard dependencies and use CI (dependency injection) to be able to test their code easily.
I'm testing a Micronaut class that has a bean injected into it. In my test I provide a #MockBean class to override it. However, it seems Micronaut still injects the real dependency.
#MicronautTest
public class ClassUnderTestTest {
#Inject ClassUnderTest classUnderTest;
#Test
public void test() {
}
#MockBean
Dependency dependency() {
return mock(Dependency.class);
}
}
I uploaded a minimum repro to Github: https://github.com/crummy/micronaut-test-dependencies . The real dependency throws an exception, and the test does too. I would not have expected this to happen because of my #MockBean.
If I change the annotation to be #MockBean(Dependency.class) then I get this error: Message: No bean of type [di.failure.example.Dependency] exists. This seems even more confusing to me - now it doesn't resolve my real or my mock dependency?
Injecting mock bean with #MockBean annotation works if your dependency in ClassUnderTest is represented by interface. Let's say Dependency is a simple interface like:
package di.failure.example;
public interface Dependency {
void run();
}
Your application may provide an implementation for this interface called DependencyImpl:
package di.failure.example;
import javax.inject.Singleton;
#Singleton
public class DependencyImpl implements Dependency {
#Override
public void run() {
throw new RuntimeException("I don't want this to load!");
}
}
Now, for test purpose you can define a mock that replaces DependencyImpl:
package di.failure.example;
import io.micronaut.test.annotation.MicronautTest;
import io.micronaut.test.annotation.MockBean;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.mockito.Mockito.mock;
#MicronautTest
public class ClassUnderTestTest {
#Inject
ClassUnderTest classUnderTest;
#Test
public void test() {
classUnderTest.run();
}
#MockBean(DependencyImpl.class)
public Dependency dependency() {
return mock(Dependency.class);
}
}
This test executes and the mock returned by dependency() method is used in place of DependencyImpl.
Using #Replaces annotation
As Sergio mentioned in the comments section you can replace class based bean dependency using #Replaces annotation. Consider following example:
package di.failure.example;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import javax.inject.Singleton;
#MicronautTest
public class ClassUnderTestTest {
#Inject
ClassUnderTest classUnderTest;
#Test
public void test() {
classUnderTest.run();
}
#Replaces(Dependency.class)
#Singleton
public static class MockDependency extends Dependency {
public MockDependency() {
System.out.println("MockDependency.<init>");
}
#Override
void run() {
System.out.println("Does not throw any exception...");
}
}
}
In this example we have defined a class MockDependency and we instruct Micronaut's DI mechanism to replace Dependency bean with MockDependency. However, there is one important thing we need to remember about - because our MockDependency extends Dependency class, parent construct gets invoked. The example you have shown in the question won't work in this case, because Dependency.<init> throws RuntimeException and the test fails. In this modified example I have used class like this one:
package di.failure.example;
import javax.inject.Singleton;
#Singleton
public class Dependency {
public Dependency() {
System.out.println("Dependency.<init>");
}
void run() {
throw new RuntimeException("I don't want this to load!");
}
}
When I run the test it passes and I see following console output:
Dependency.<init>
MockDependency.<init>
Does not throw any exception...
The main difference comparing to #MockBean is that in case of #Replaces you are using a concrete class object. As a workaround (if we really need a Mockito mock object) is to create a mock internally and delegate calls to this object, something like this:
#Replaces(Dependency.class)
#Singleton
public class MockDependency extends Dependency {
private final Dependency delegate;
public MockDependency() {
this.delegate = mock(Dependency.class);
}
#Override
void run() {
delegate.run();
}
}
I had the case - controller MyController with service MyService.
#MockBean(MyServiceImpl.class) did't mock injected service.
when(myService.doSomething()).thenReturn... immediately called real method.
I fixed mocking issue with giving MyService-bean a name and passing this name to #MockBean(name = )
controller:
#Controller
public class MyController {
private MyService myService;
...
}
factory:
#Factory
public class MyFactory {
#Named("myService") // Named just because #MockBean didn't work without it
#Context
public MyService myService() {
return new MyServiceImpl();
}
}
test:
#MicronautTest
class MyControllerTest {
#Inject
MyService myService;
#Inject
#Client("/")
#HttpClient client;
#MockBean(named = "myService")
MyService mockMyService() {
return mock(MyService.class);
}
#Test
void test() {
when(myService.doSomething()).thenReturn(genDto());
...
}
}
Details:
micronaut-test-junit5 3.1.1
junit-jupiter-api 5.6.2
Trying to test a fairly simple JAX-RS endpoint
#ApplicationScoped
#Path("mypath")
public class MyRestService {
#Inject
private Logger logger;
#Inject
private EjbService ejbService;
#GET
public String myMethod() {
logger.info("...");
return ejbService.myMethod();
}
}
with Mockito and Jersey Test
#RunWith(MockitoJUnitRunner.class)
public class MyRestServiceTest extends JerseyTest {
#Mock
private EjbService ejbService;
#Mock
private Logger logger;
#InjectMocks
private MyRestService myRestService;
...
#Override
protected Application configure() {
MockitoAnnotations.initMocks(this);
return new ResourceConfig().register(myRestService);
}
}
The Grizzly container is returning a org.glassfish.hk2.api.UnsatisfiedDependencyException for Logger and EjbService even thought the dependencies are injected correctly by Mockito.
Seems Grizzly is trying, correctly, to ovverride the Mockito mocks.
If I register an AbstractBinder in the configure method, everything works fine.
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(ejbService).to(EjbService.class);
bind(logger).to(Logger.class);
}
});
But I don't feel it's the best way to accomplish injection. Mockito style is better imho.
What do I need to do to solve this issue?
I was able to create the following base class in order to achieve integration between JerseyTest and Mockito such as the OP aimed for:
package org.itest;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.JerseyTestNg;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.util.ReflectionUtils;
import javax.ws.rs.core.Application;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* #author Nom1fan
*/
public abstract class JerseyTestBase extends JerseyTestNg.ContainerPerClassTest {
#Override
protected Application configure() {
MockitoAnnotations.openMocks(this);
ResourceConfig application = new ResourceConfig();
Object resourceUnderTest = getResourceUnderTest();
application.register(resourceUnderTest);
Map<String, Object> properties = Maps.newHashMap();
properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
properties.put("contextConfigLocation", "classpath:applicationContext.xml");
// Retrieve the fields annotated on subclass as #Mock via reflection and keep each instance
// and its type on an entry in the map, later used to bind to Jersey infra.
HashMap<Object, Class<?>> mocksToBindMap = Maps.newHashMap();
List<Field> fieldsWithMockAnnotation = FieldUtils.getFieldsListWithAnnotation(getClass(), Mock.class);
for (Field declaredField : fieldsWithMockAnnotation) {
declaredField.setAccessible(true);
Object fieldObj = ReflectionUtils.getField(declaredField, this);
mocksToBindMap.put(fieldObj, declaredField.getType());
}
application.setProperties(properties);
application.register(new AbstractBinder() {
#Override
protected void configure() {
for (Map.Entry<Object, Class<?>> mockToBind : mocksToBindMap.entrySet()) {
bind(mockToBind.getKey()).to(mockToBind.getValue());
}
}
});
return application;
}
protected abstract Object getResourceUnderTest();
}
The hook getResourceUnderTest must be implemented by the extending test class, providing the instance of the resource it wishes to test.
Test class example:
import org.itest.JerseyTestBase;
import org.mockito.InjectMocks;
import org.mockito.Mock;
public class MyJerseyTest extends JerseyTestBase {
#Mock
private MockA mockA;
#Mock
private MockB mockB;
#InjectMocks
private MyResource myResource;
#Override
protected Object getResourceUnderTest() {
return myResource;
}
#Test
public void myTest() {
when(mockA.foo()).thenReturn("Don't you dare go hollow");
when(mockB.bar()).thenReturn("Praise the Sun \\[T]/");
// Test stuff
target("url...").request()...
}
}
MyResource class looks something like this:
#Path("url...")
#Controller
public class MyResource {
private final MockA mockA;
private final MockB mockB;
#Autowired // Mocks should get injected here
public MyResource(MockA mockA, MockB mockB) {
this.mockA = mockA;
this.mockB = mockB;
}
#GET
public Response someAPI() {
mockA.foo();
mockB.bar();
}
}
NOTE: I used Spring's and Apache's reflection utils to make things easier but it's not mandatory. Simple reflection code which can be written by hand.
The MockitoJUnitRunner is for unit tests and JerseyTest is for integration tests.
When using Mockito, your tests will call directly the declared myRestService and Mockito dependency injection will take place.
When using JerseyTest, a new web container is created and your tests talk to MyRestService via an HTTP call. Inside this container, the real dependency injection is happening, the classes are not even seeing you declared mocks.
You can use JerseyTest and Mockito together, exactly as you did. It just requires some extra configurations (as you already found) and the #RunWith annotation is not necessary.
I know I should not be testing void methods like this, but I am just testing Mockito.doNothing() as of now with a simple example.
My Service class:
#Service
public class Service{
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscribtions(){
kafkaConsumer.unsubscribe();
}
}
My Test class:
#MockBean
private Consumer<String, String> kafkaConsumer;
#Test
public void testClearSubscriptions() {
Service service = new Service();
Mockito.doNothing().when(kafkaConsumer).unsubscribe();
service.clearSubscriptions();
}
The test keeps failing with a null pointer exception. When I debugged it, it goes into the clearSubscription method of the service class, and there on the line of kafkaConsumer.unsubscribe(), kafkaConsumer is null. But I mocked the consumer, why is it throwing null pointer exception and I should be skipping over that method, right?
Edit:
All the declarations of the class:
#Autowired
private Consumer<String, String> kafkaConsumer;
#Autowired
private Service2 service2;
private final Object lock = new Object();
private static Logger logger = LoggerFactory.getLogger(Service.class);
private HashMap<String, String> subscribedTopics = new HashMap<>();
Figured out what was wrong, I needed to auto wire the service
You are instantiating a new service Service service = new Service(); but from what I can see you are never injecting the mock bean into the new service.
Here is a sample of what I think you could do if you are using mockito only and dont need to instantiate a spring container (used a single class for ease of example dont do this in actual code):
package com.sbp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#RunWith(MockitoJUnitRunner.class) // run with mockitos runner so annotations are processed
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
#Mock // tell mockito that this is a mock class - it will instantiate for you
private Consumer<String, String> kafkaConsumer;
#InjectMocks // tell mockito to inject the above mock into the class under test
private MyService service = new MyService();
#Test
public void testClearSubscriptions() {
service.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}
If you need an example via Spring using MockBean or without and dependencies, let me know and I can post.
UPDATED: adding sample using spring junit runner and using spring boot's mockbean annotation
package com.sbp;
import com.sbp.MyServiceTest.TestContext.MyService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class) // run with spring
#SpringBootTest(classes = MyServiceTest.TestContext.class) // make it a spring boot test so #MockBean annotation is processed, provide a dummy test context class
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Configuration
public static class TestContext {
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
}
#MockBean // this will create a mockito bean and put it in the application context in place of the Kafka consumer bean defined in the TestContext class
private Consumer<String, String> kafkaConsumer;
#Autowired // inject the bean from the application context that is wired with the mock bean
private MyService myService;
#Test
public void testClearSubscriptions() {
myService.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}
Suppose I have a class like so:
public class StaticDude{
public static Object getGroove() {
// ... some complex logic which returns an object
};
}
How do I mock the static method call using easy mock? StaticDude.getGroove().
I am using easy mock 3.0
Not sure how to with pure EasyMock, but consider using the PowerMock extensions to EasyMock.
It has a lot of cool functions for doing just what you need -
https://github.com/jayway/powermock/wiki/MockStatic
Easymock is a testing framework for "for interfaces (and objects through the class extension)" so you can mock a class without an interface. Consider creating an interfaced object with an accessor for your static class and then mock that acessor instead.
EDIT: Btw, I wouldn't recommend doing static classes. It is better to have everything interfaced if you are doing TDD.
Just in Case PowerMock is unavailable for any reason:
You could move the static call to a method, override this method in the instantiation of the tested class in the test class, create a local interface in the test class and use its method in the overidden method:
private interface IMocker
{
boolean doSomething();
}
IMocker imocker = EasyMock.createMock(IMocker.class);
...
#Override
void doSomething()
{
imocker.doSomething();
}
...
EasyMock.expect(imocker.doSomething()).andReturn(true);
Generally speaking, it is not possible to mock a static method without using some sort of accessor, which seems to defeat the purpose of using a static method. It can be quite frustrating.
There is one tool that I know of called "TypeMock Isolator" which uses some sort of Satanic Magic to mock static methods, but that tool is quite expensive.
The problem is, I know of no way to override a static method. You can't declare it virtual. you can't include it in an interface.
Sorry to be a negative nelly.
Adding an exemple on how to implements static mock along regular mock of injected classes with EasyMock / PowerMock, since the linked exemple only shows static mock.
And with the PowerMockRunner the #Mock services are not wired on the #TestSubject service to test.
Let say we have a service we want to test, ServiceOne :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class ServiceOne {
#Autowired
private ServiceTwo serviceTwo;
public String methodToTest() {
String returnServ2 = serviceTwo.methodToMock();
return ServiceUtils.addPlus(returnServ2);
}
}
Which calls another service that we will want to mock, ServiceTwo :
import org.springframework.stereotype.Service;
#Service
public class ServiceTwo {
public String methodToMock() {
return "ServiceTwoReturn";
}
}
And which calls a final class static method, ServiceUtils :
public final class ServiceUtils {
public static String addPlus(String pParam) {
return "+" + pParam;
}
}
When calling ServiceOne.methodToTest() we get "+ServiceTwoReturn" as a return.
Junit Test with EasyMock, mocking only the injected ServiceTwo Spring service :
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMockRunner;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(EasyMockRunner.class)
public class ExempleTest {
#TestSubject
private ServiceOne serviceToTest = new ServiceOne();
#Mock
private ServiceTwo serviceMocked;
#Test
public void testMethodToTest() {
String mockedReturn = "return2";
expect(serviceMocked.methodToMock()).andReturn(mockedReturn);
replay(serviceMocked);
String result = serviceToTest.methodToTest();
verify(serviceMocked);
assertEquals("+" + mockedReturn, result);
}
}
Junit Test with EasyMock & PowerMock, mocking the injected ServiceTwo Spring service but also the final class and its Static method :
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.mockStatic;
import static org.powermock.reflect.Whitebox.setInternalState;
import org.easymock.Mock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ServiceUtils.class)
public class ExempleTest {
private ServiceOne serviceToTest;
private ServiceTwo serviceMocked;
#Before
public void setUp() {
serviceToTest = new ServiceOne();
serviceMocked = createMock(ServiceTwo.class);
// This will wire the serviced mocked into the service to test
setInternalState(serviceToTest, serviceMocked);
mockStatic(ServiceUtils.class);
}
#Test
public void testMethodToTest() {
String mockedReturn = "return2";
String mockedStaticReturn = "returnStatic";
expect(serviceMocked.methodToMock()).andReturn(mockedReturn);
expect(ServiceUtils.addPlus(mockedReturn)).andReturn(mockedStaticReturn);
PowerMock.replayAll();
String result = serviceToTest.methodToTest();
PowerMock.verifyAll();
assertEquals(mockedStaticReturn, result);
}
}