finalize() should be treated specially
Brought to you by:
tammofreese
If I lose all references to a mock in a test, it may (or may not) get GCed before the test is over. If it does happen, this creates an expectation of a call to finalize(), making the test fail. Since the GC is unpredictable, this leads to flaky tests.
We should treat finalize() specially, probably by ignoring it completely, since it makes no sense to expect it given that there's no way to force GC.
I can't see how one can loose a reference to a mock since you usually a least have a local variable in the test for it...
But, yes, I guess finalize should be treated specially. Probably be doing nothing.
You can argue that it's a bad testing practice, but I've seen tests that do like this:
subject = new TestSubject(mockControl.createMock(SomeClass.class));
then
expect(subject.getSomething().someMethod()).andReturn(...)
or even using package-visible attributes:
expect(subject.something.someMethod()).andReturn(...)
which is ugly but works, except if the field "something" gets reassigned to something else. If that happens the test won't fail consistently, but rather become flaky.
I'm guessing you had the problem on class mocking? (as opposed to interface mocking)
Yes, it was while using class extension. I'm not familiar enough with the internals of mocking interfaces to know if it'd also happen there.
I am seeing this also.
The issue is that the local variable has lost scope but EasyMock is still tracking it. Here is an example pseudo code to illustrate:
@Before
Public void setup()
{
reset();
// common expects here
}
@Test
Public void test1()
{
Object mockobj = createConcreteMock(Object.class);
// some expects here
replay();
// some code here here
verify(); // all good here
// some asserts here
}
@Test
Public void test2()
{
// expects and setup here if needed
// garbage collection happens here
replay();
// some code here
// garbage collection could happen here too
verify(); // unexpected method call finalize() here on mockObj
// some asserts here
}
If EasyMock was to hold on to a reference of the object as its tracking them then the mock objects would never be garbage collected during the life of the test and this would not happen. Makes me almost want to download the code just to see how it is able to track the objects without a reference <bg>. </bg>
One scenario where this could happen, is after the test method goes out of scope but the entire test suite is still running.
When gc collects a mock, it will call finalize(), which then will fail because it's an unexpected method call.
Another scenario, is when someone uses an IMocksControl explicitly, and call control.createMock() to create a dummy that he doesn't expect any method to be called:
foo(control.createMock(Service.class));
control.replayAll();
The mock goes out of scope after foo() finishes, so gc could invoke finalize() any time to break the test.
see http://jira.codehaus.org/browse/EASYMOCK-21