I have a class which writes messages to some logs. The class is a utility which doesn't do anything else, it runs in the background, checks a few things and logs them. I'm wondering if it's possible for me to verify in a unit test that the log has been written to without caring about what it is actually writing. Here's my class being tested:
//imports...
public class MyClass {
private static Log log = LogFactory.getLog(MyClass.class);
public MyClass() {
log.info("MyClass is being created.");
}
public void doThing() {
if( everything_is_fine ) {
log.info("This is a message to say everything is fine.");
} else {
log.error("Uh oh...");
}
}
}
And my tester class:
// imports ...
@RunWith(PowerMockRunner.class)
@PrepareForTest({MyClass.class,LogFactory.class})
public class MyClassTest {
Log mockLog;
@Before
public void setup() {
PowerMockito.mockStatic(LogFactory.class);
mockLog = mock(Log.class);
PowerMockito.when(LogFactory.getLog(MyClass.class)).thenReturn(mockLog);
}
@Test
public void test_everything_is_ok() {
MyClass mything = new MyClass(); // should write to log.info
mything.doThing(); // should write to log.info
verify(mockLog, atLeastOnce()).info(anyString());
verify(mockLog, never()).error(anyString());
}
@Test
public void test_everything_is_not_ok() {
MyClass mything = new MyClass(); // should write to log.info
// do something which makes things not ok
mything.doThing(); // should write to log.error
verify(mockLog, atLeastOnce()).info(anyString());
verify(mockLog, atLeastOnce()).error(anyString());
}
}
When I run the tests, I expect that the log.info() is invoked for both tests, and the log.error() is invoked only for the second. However I'm getting a "Wanted but not invoked" for the log.info for both tests. and for log.error on the second. So either:
1) My code is broken and not writing to the log, or
2) My test is broken.
I'm thinking that I've messed up something in my test, probably something really obvious, so has anyone had experience testing something like this who could help me out? Any and all help will be appreciated.
UPDATE:
Thanks to those who helped out, I've got a solution now.
After playing around with the code for a bit, I discovered that there seems to be an issue with the initialization of the log. Doing private static Log log = LogFactory.getLog(MyClass.class);
didn't seem to use the mock correctly, so if I move it to the constructor it seemed to be mocked OK and my tests all work as expected:
public class MyClass {
private static Log log;
public MyClass() {
MyClass.log = LogFactory.getLog(MyClass.class);
log.info("MyClass is being created.");
}
// etc ...
}
I've got it working now, but can anyone explain why initializing the log the first way didn't work? Or perhaps point me to somewhere which explains it? I'm not sure if there's a gap in my understanding of how Java initializes objects or if it's a limitation of mocking frameworks.