I need to mock a logger class on which the classes under test call a static method to obtain an object. Because of this I created a fake implementation of that static function as described in the official tutorial. My fake class looks like this
public class FakeLogger extends MockUp<Logger> {
#Mocked Logger logger;
#Mock
public Logger getLogger() {
return logger;
}
}
It is used in the test class:
public class MyTest {
#Mocked Logger logger;
#Before
public void setUp() throws Exception {
new FakeLogger();
}
#Test
public void test() {
new Expectations() {{
logger.warn("message");
times = 1;
}};
/*here classes under test are executed which obtain use the logger by Logger.getLogger().log("message");*/}
This implementation does not work.
The problem is that Logger.getLogger() returns null because in the fake implementation the object #Mocked Logger logger; seems to be never initialized.
Then I tried the following changes:
public class FakeLogger extends MockUp<Logger> {
#Mocked Logger logger;
public FakeLogger(Logger l) {
logger=l;
}
#Mock
public Logger getRootLogger() {
return logger;
}
}
With the test class initializing the fake implementation:
public class MyTest {
#Mocked Logger logger;
#Before
public void setUp() throws Exception {
new FakeLogger(logger);
}
#Test
public void test() {
new Expectations() {{
logger.warn("message");
times = 1;
}};
...}
When initializing the fake implementation class with the #Mocked Logger logger; object from the test class it works without a null pointer exception from an uninitialized logger object.
My conclusion would be that #Mocked Logger logger; is only initialized automatically inside the test class but not in the fake class. But according to the official tutorial #Mocked object should always be initialized automatically.
What am I missing here?
When JMockit runs, variables annotated with #Tested or #Mocked should be non-null. Effectively, JMockit is creating a MockImpl for each of them. Ignoring a lot of the complexity of your post, I focused mostly on the fact your #Mocked logger is null, which makes me strongly suspect you aren't running with JMockit. Remember that in order to run properly, you have to have "-javaagent" stuff. Without it, those #Mocked don't work and the variable will be null. I'm guessing that's what's happening.
Related
Given the following classes, I would like to test the first one. Where I'm running into trouble is that because these are used in AWS Lambda, services are architected a certain way for performance reasons (e.g. container reuse) and I'm finding it impossible to even get an empty test class to run thanks to the static initializer.
public class ClassToTest {
private static ServiceA serviceA = new ServiceA();
public boolean methodToTest() {
return serviceA.someMethod();
}
}
public class ServiceA {
public ServiceA() {
this(new ServiceB());
}
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
this.someVariable = serviceB.getExpensiveServiceData();
}
}
public class ServiceB{
private static final ExpensiveService expensiveService;
private static final Map<String, Object> expensiveServiceData;
static {
expensiveService = new ExpensiveService();
expensiveServiceData = expensiveService.getData();
}
public getExpensiveServiceData() {
return expensiveServiceData.get("...");
}
}
We use Mockito for our tests. I have added PowerMockito and set up my test as follows:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor({"com.x.ServiceA", "com.x.ServiceB"})
#PrepareForTest({ServiceA.class, ServiceB.class})
public class ClassToTestTest {
#InjectMocks private ClassToTest classToTest;
#Mock // All the classes...
public void methodToTestTest() {
// Doesn't even get here
classToTest.methodToTest();
}
}
This fails due to a NPE in getExpensiveServiceData() because I'm ignoring the static initializer and thus the Map never gets created. And I can't use Whitebox.setInternal to set the value programmatically because the class fails initialization before I have access to that. I assume ServiceA shouldn't actually be calling the real implementation of ServiceB but I don't know how to inject a mock into a mock like that in my test.
I'm adding logging functionality to my application and I'm using slf4j implemented by log4j. To simplify my code I have a helper class to produce a logger (the idea is not mine, I took it from Beginning Java EE 7 by Antonio Goncalves), the class is like this:
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerProducer {
#Produces
public Logger createLogger(InjectionPoint injPoint) {
return LoggerFactory.getLogger(injPoint.getMember().getDeclaringClass());
}
}
So in my classes all I have to do (in theory) is:
public class SomeClass {
#Inject private Logger logger; // this could be private or default, doesn't matter
}
The problem is that during test phase I don't have CDI enabled because I'm running the code outside the container. I can't inject manually because I have some abstract classes with their own logger, so I can't instantiate that class and assign it a Logger instance.
Is there any way to "enable" CDI during test phase? My project is built with Maven and I'll be deploying to a Wildfly 10 Server. Thanks in advance for your answers.
Note
I cannot do something like
public class SomeClassTest {
private SomeClass someClass; // this is the class I want to test
#Before
public void init() {
someClass.logger = LoggerFactory.getLogger(SomeClass.class);
}
#Test
public void someTest(){...}
}
because I have some abstract classes with their own private Logger logger property and I need to keep it that way (I can't declare it protected ) because I want to keep a trace of where a message is thrown, so I need to keep the logger private. E.g. I have something like
public abstract class MyAbstract {
#Inject
private Logger logger;
}
public class MyConcrete extends MyAbstract {
#Inject
private Logger logger;
}
I could, from the test class, set MyConcrete.logger as default but I cant do that forMyAbstract.loggerbecause they are in different packages and I can't instantiateMyAbstract`, am I explaining correctly?
Note 2
My project structure is something like this:
package common
import org.slf4j.logger
...
public abstract class Generic {
#Inject
private Logger logger;
public void doSomethingGeneric(){
// do something and log it
}
}
package specific
import ...
public class Concrete extends Generic{
#Inject
Logger logger;
// some concrete methods...
}
package specific
public class ConcreteTest {
private Concrete concrete;
#Before
public void init() {
concrete = new Concrete();
concrete.logger = LoggerFactory.getLogger(Concrete.class); // Dependency injection by hand
}
// some #Test methods
}
Eventually, a #Test method from ConcreteTest calls Concrete.doSomethingGeneric() as it's inherited to the concrete instance. The problem is that doSomethingGeneric() logs with the Generic logger and I don't know how to satisfy that dependency in a clean way (I prefer not to hack my code with setter methods unnecessary for production)
You can do this, using mockito test units:
#RunWith(MockitoJUnitRunner.class)
public class MyBeanTest {
#Spy
private Logger logger = LoggerProducer.getLogger();
#InjectMocks
private MyBean myTestBean; // = new MyBean(parameters). explicit if no default constructir
#Test
public void verifySomeLogic() {
myTestBean.doSomething();
}
}
Here, the mockito runtime and proxy will manage the injection, and you can even do verification on the logger itself.
I am trying to set up my class to be used in Junit.
However when I try to do the below I get an error.
Current Test Class:
public class PersonServiceTest {
#Autowired
#InjectMocks
PersonService personService;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
assertThat(PersonService, notNullValue());
}
//tests
Error:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'personService'
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
How can I fix this?
You are not mocking anything in your code. #InjectMocks sets a class where a mock will be injected.
Your code should look like this
public class PersonServiceTest {
#InjectMocks
PersonService personService;
#Mock
MockedClass myMock;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Mockito.doReturn("Whatever you want returned").when(myMock).mockMethod;
}
#Test()
public void testPerson() {
assertThat(personService.method, "what you expect");
}
Another solution is to use #ContextConfiguration annotation with static inner configuration class like so:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class PersonServiceTest {
#Autowired
PersonService personService;
#Before
public void setUp() throws Exception {
when(personService.mockedMethod()).thenReturn("something to return");
}
#Test
public void testPerson() {
assertThat(personService.method(), "what you expect");
}
#Configuration
static class ContextConfiguration {
#Bean
public PersonService personService() {
return mock(PersonService.class);
}
}
}
Anyway, you need to mock something that the method you want to test uses inside to get desired behaviour of that method. It doesn't make sense to mock the service you're testing.
You're misunderstanding the purpose of the mock here.
When you mock out a class like this, you are pretending as if it's been injected into your application. That means you don't want to inject it!
The solution to this: set up whatever bean you were intending to inject as #Mock, and inject them into your test class via #InjectMocks.
It's unclear where the bean you want to inject is since all you have is the service defined, but...
#RunWith(MockitoJUnitRunner.class);
public class PersonServiceTest {
#Mock
private ExternalService externalSvc;
#InjectMocks
PersonService testObj;
}
If I am not mistaken...the thumb rule is you cannot use both together..you either run unit test cases with using MockitojunitRunner or SpringJUnitRunner you cannot use both of them together.
I have a utility class which is a final class. There i have used injection for injecting LOGGER.
public final class Utilities {
#Inject
private static Logger.ALogger LOGGER;
private Utilities() {
//this is the default constructor. so there is no implementation
}
public static String convertToURl(string input){
try{
//do some job
}catch(IllegalArgumentException ex){
LOGGER.error("Invalid Format", ex);
}
}
}
While I writing unit testing for this method i have to mock LOGGER otherwise it will throw null pointer exception. How can i mock this LOGGER without creating instance of this class. I tried to whitebox the variable. But it only works with instances?
This code works fine. To set static field you need to pass a class to org.powermock.reflect.Whitebox.setInternalState. Please, ensure that you use PowerMock's class from the package org.powermock.reflect because Mockito has the class with the same name.
#RunWith(PowerMockRunner.class)
public class UtilitiesTest {
#Mock
private Logger.ALogger aLogger;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); // for case them used another runner
Whitebox.setInternalState(CcpProcessorUtilities.class, "LOGGER", aLogger);
}
#Test
public void testLogger() throws Exception {
Utilities.convertToURl("");
verify(aLogger).error(eq("Invalid Format"), any(IllegalArgumentException.class));
}
}
I'm writing a unit test for a class A which extends class B. I'm using Mockito and I want to mock a org.slf4j.logger both classes to use. The problem is when class A calls on a method from class B, the mocked logger is not injected into class B so I get a NPE. Is there a way to successfully test this?
public class ClassA extends ClassB {
#Inject
private static final Logger LOGGER = LoggerFactory.getLogger(ClassA.class);
public void classAMethod {
LOGGER.debug("u wot m8");
this.classBMethod();
}
public class ClassB {
#Inject
private static final Logger LOGGER = LoggerFactory.getLogger(ClassB.class);
public void classBMethod {
LOGGER.debug("u wot m8");
}
}
public class ClassATest {
#InjectMocks
private ClassA classA = new ClassA
#Mock
private Logger mockLogger;
#Test
public void testClassA() {
classA.classAMethod ();
verify(mockLogger, (times, 2)).debug(Mockito.anyString());
}
}
Mockito can't inject static final fields. In fact, nobody can, since by definition, a final field can't be assigned twice. And Mockito doesn't inject static fields anyway. Only instance fields.
See the documentation:
However fields that are static or final will be ignored.
If you are using Java EE 6 #Inject it will not work outside the CDI container, this is part of the spec. If this is a unit test you have to provide it by yourself.