Menu

#18 finalize() should be treated specially

EasyMock_2.4
closed
5
2012-10-05
2009-03-24
No

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.

Discussion

  • Henri Tremblay

    Henri Tremblay - 2009-03-28

    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.

     
  • Rodrigo Damazio

    Rodrigo Damazio - 2009-03-30

    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.

     
  • Henri Tremblay

    Henri Tremblay - 2009-04-05

    I'm guessing you had the problem on class mocking? (as opposed to interface mocking)

     
  • Rodrigo Damazio

    Rodrigo Damazio - 2009-04-06

    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.

     
  • Scott Conway

    Scott Conway - 2010-03-04

    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>

     
  • Ben Yu

    Ben Yu - 2010-06-25

    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.

     

Log in to post a comment.

MongoDB Logo MongoDB