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.
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 am having one #Inject object which i need to mock its throwing NPE while using #InjectMocks at my test class
Below is the structure of my class
Class ChildClass extends ParentClass
{
public myMethod(){
String myval=callParentClassMethod();
}
Class ParentClass{
#Inject
private MyService myservice;
//there is no constructor or setter method for this injected object
protected String callParentClassMethod(){
retrun myservice.getStringval();
}
}
Can you please suggest how to mock this MyService object.I have used #Mock and #Injected mock but its not working getting NPE. i tried to inject in both Base class and parent class but no luck.
following works for me:
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#InjectMocks
private ChildClass childClass = new ChildClass();
#Mock
private MyService service;
#Test
public void test1() {
childClass.myMethod();
}
}
UPDATE
I've uploaded example project with test to https://github.com/shurick-k/mock-test
You've not specified exactly what type of #Inject annotation is used, so I assumed it is from javaee-api. Actually it does not matter.
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 have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}
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.