I have a class with SLF4J logger instanced like:
public class MyClass {
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
public void foo() {
log.warn("My warn");
}
}
And I need to test it with JMockit like:
@Test
public void shouldLogWarn(@Mocked Logger log) throws Exception {
new Expectations() {{
log.warn(anyString);
}};
MyClass my = new MyClass();
my.foo();
}
After searching a lot I figured out, I need to use MockUp somehow. But can't get it how exactly.
Btw, I'm using last version of JMockit(1.29) where you no more can setField(log) for final static fields.
@Capturing
annotation that works for this situationIndicates a mock field or a mock parameter for which all classes extending/implementing the mocked type will also get mocked.
Future instances of a capturing mocked type (ie, instances created sometime later during the test) will become associated with the mock field/parameter. When recording or verifying expectations on the mock field/parameter, these associated instances are regarded as equivalent to the original mocked instance created for the mock field/parameter.
This means that if you annotate it with @Capturing
instead of @Mocked
, every Logger
that is created during the test run will be associated with one you annotated. So the following works:
@Test
public void shouldLogWarn(@Capturing final Logger logger) throws Exception {
// This really ought to be a Verifications block instead
new Expectations() {{
logger.warn(anyString);
}};
MyClass my = new MyClass();
my.foo();
}
As a side note, if all you want to do is verify that a method is called, it's better to use Verifications instead, since that is what it is intended for. So your code would look like this:
@Test
public void shouldLogWarn(@Capturing final Logger logger) throws Exception {
MyClass my = new MyClass();
my.foo();
new Verifications() {{
logger.warn(anyString);
}};
}
@Mocked
on both Logger
and LoggerFactory
In some cases, @Capturing
won't work as intended due to intricacies of how the annotation works. Fortunately, you can also get the same effect by using @Mocked
on both Logger
and LoggerFactory
like so:
@Test
public void shouldLogWarn(@Mocked final LoggerFactory loggerFactory, @Mocked final Logger logger) throws Exception {
MyClass my = new MyClass();
my.foo();
new Verifications() {{
logger.warn(anyString);
}};
}
Note: JMockit 1.34 through 1.38 has a bug that prevents this from working with slf4j-log4j12
, and possibly other dependencies of SLF4J. Upgrade to 1.39 or later if you run into this bug.