jmockit / jmockit1

Advanced Java library for integration testing, mocking, faking, and code coverage
Other
465 stars 240 forks source link

Capturing interfaces fails because of parameter order #20

Closed nsinkov closed 10 years ago

nsinkov commented 10 years ago

I have two interfaces and I am mocking one method on each one. I'm using the @Capturing annotation. But simply having the @Mocked parameters in a different order causes one of the interfaces to not be mocked.

(The same happens if the @Mocked parameters are fields of the test class - the order matters)

The code below has two identical tests. One fails only because the parameters are in a different order. (I believe that both tests should pass)

code:

import mockit.Capturing; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.integration.junit4.JMockit;

import org.junit.Test; import org.junit.runner.RunWith;

@RunWith(JMockit.class) public class InterfaceMocks {

public static interface I1 {
    Double num();
    String other();
}

public static interface I2 {
    I1 getI1();
    String method();
}

@Test
public void testGood(@Mocked("num") @Capturing final I1 i1Mock, @Mocked("getI1") @Capturing final I2 i2Mock) {
    new NonStrictExpectations() {
        {
            i1Mock.num();
            result = null;

            i2Mock.getI1();
            result = null;
        }
    };

    I2 i2 = new I2() {
        @Override
        public I1 getI1() {
            throw new RuntimeException();
        }

        @Override
        public String method() {
            throw new RuntimeException();
        }
    };

    I1 i1 = i2.getI1();

}

@Test
public void testBad(@Mocked("getI1") @Capturing final I2 i2Mock, @Mocked("num") @Capturing final I1 i1Mock) {
    new NonStrictExpectations() {
        {
            i1Mock.num();
            result = null;

            i2Mock.getI1();
            result = null;
        }
    };

    I2 i2 = new I2() {
        @Override
        public I1 getI1() {
            throw new RuntimeException();
        }

        @Override
        public String method() {
            throw new RuntimeException();
        }
    };

    I1 i1 = i2.getI1();

}

}

rliesenfeld commented 10 years ago

Yes, seems like there is a subtle bug here; I will look into it.

Two details in the example tests catch the attention, though; if you can clarify I would appreciate: 1) Why the partial mocking with @Mocked("num"), etc.? Does the (real) test actually need to execute the real code of some methods, while others are mocked? 2) null is the default result for any method returning a reference type, so recording "result = null" for them is redundant. Or is there something I missed?

nsinkov commented 10 years ago

"Does the (real) test actually need to execute the real code of some methods, while others are mocked?" correct

"null is the default result for any method returning a reference type, so recording "result = null" for them is redundant. Or is there something I missed?" yeah, that's just for this test code - not null in the real code