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.
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 for
MyAbstract.loggerbecause they are in different packages and I can't instantiate
MyAbstract`, am I explaining correctly?
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.