Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injectable and Mock static methods of the same class #1

Closed
ColinHebert opened this issue Jul 11, 2014 · 7 comments
Closed

Injectable and Mock static methods of the same class #1

ColinHebert opened this issue Jul 11, 2014 · 7 comments
Assignees
Labels

Comments

@ColinHebert
Copy link

@ColinHebert ColinHebert commented Jul 11, 2014

This test used to work with JMockit 1.8, stopped working with JMockit 1.9

public class InetAddressTest {
    @Injectable
    private InetAddress mockInetAddress;

    @Test
    public void testInetAddressMock() throws Exception{
        new NonStrictExpectations(InetAddress.class) {{
            InetAddress.getLocalHost();
            result = mockInetAddress;
        }};

        Assert.assertSame(InetAddress.getLocalHost(), mockInetAddress);
    }
}

The problem here is that JMockit complains about the class being already mocked.

Class is already mocked: class java.net.InetAddress

This is fair enough, but what is the recommended approach when I need to mock a static method from a class while having an Injectable value as well?

@rliesenfeld

This comment has been minimized.

Copy link
Member

@rliesenfeld rliesenfeld commented Jul 14, 2014

You can write the test with @Cascading, which is simpler:

public class InetAddressTest {
@Cascading
private InetAddress mockInetAddress;

@Test
public void testInetAddressMock() throws Exception {
    Assert.assertSame(InetAddress.getLocalHost(), mockInetAddress);
}

}

That said, not mocking InetAddress would be better, if that's an option.

On Fri, Jul 11, 2014 at 8:39 PM, Colin Hebert notifications@github.com
wrote:

This test used to work with JMockit 1.8, stopped working with JMockit 1.9

public class InetAddressTest {
@Injectable
private InetAddress mockInetAddress;

@Test
public void testInetAddressMock() throws Exception{
    new NonStrictExpectations(InetAddress.class) {{
        InetAddress.getLocalHost();
        result = mockInetAddress;
    }};

    Assert.assertSame(InetAddress.getLocalHost(), mockInetAddress);
}

}

The problem here is that JMockit complains about the class being already
mocked.

Class is already mocked: class java.net.InetAddress

This is fair enough, but what is the recommended approach when I need to
mock a static method from a class while having an Injectable value as
well?


Reply to this email directly or view it on GitHub
#1.

@ColinHebert

This comment has been minimized.

Copy link
Author

@ColinHebert ColinHebert commented Jul 14, 2014

I'd love to not mock InetAddress, the problem is, InetAddress.getLocalHost().getCanonicalHostName() in Java does a lookup on the hostname which may take quite some time (or even fail) depending on some network settings. To make sure this is handled properly by the application I do not have a better solution right now.

@ColinHebert

This comment has been minimized.

Copy link
Author

@ColinHebert ColinHebert commented Jul 14, 2014

Regarding the solution itself, my bad I should have given a more detailed example of what I need:

public class InetAddressTest {
    @Injectable
    private InetAddress mockInetAddressLocal;
    @Injectable
    private InetAddress mockInetAddressRemote;

    @Test
    public void testInetAddressMock() throws Exception {
        new NonStrictExpectations(InetAddress.class) {{
            InetAddress.getLocalHost();
            result = mockInetAddressLocal;
            InetAddress.getByName("bing.com");
            result = mockInetAddressRemote;
        }};

        Assert.assertSame(InetAddress.getLocalHost(), mockInetAddressLocal);
        Assert.assertSame(InetAddress.getByName("bing.com"), mockInetAddressRemote);
        Assert.assertNotSame(InetAddress.getByName("bing.com"), InetAddress.getLocalHost());
        // This is doing an actual resolution, not using any mocks.
        Assert.assertEquals(InetAddress.getByName("reddit.com").getCanonicalHostName(), "reddit.com");
    }
}

In this case, the resolution to reddit.com still works with the default behaviour (using 1.8), which is perfect as it's exactly what I need. The problem with mocking classes such as InetAddress is that some subsystems might depend on it, so the less modifications to the default behaviour, the better.

EDIT: changed google.com to reddit.com (google tends to have servers with different hostnames), that doesn't change the point though.

@rliesenfeld

This comment has been minimized.

Copy link
Member

@rliesenfeld rliesenfeld commented Jul 14, 2014

This is a tough one.

If the duplicate mocking of InetAddress is allowed, then what to make of a call to "mockInetAddressRemote.getCanonicalHostName()"? Should it be mocked according to the @Injectable mocking, or according to the partial mocking? In the first case, this method call would simply return null without doing anything; in the second, it would execute the real implementation of "getCanonicalHostName()", since no expectation was recorded for it.

Incidentally, I noticed that if that call is made using JMockit 1.8, a JVM crash occurs! I will need to investigate it later.

@ColinHebert

This comment has been minimized.

Copy link
Author

@ColinHebert ColinHebert commented Jul 14, 2014

I would expect the first behaviour. The @Injectable behaviour should take precedence over the partial mocking (so null as a result).
Not because it fits my use case, but because I would expect @Injectable to be considered as "more specific" (applies to one instance) whereas partial mocking (or @Mocked for that matter) is more broad (applies to the entire class).

@rliesenfeld

This comment has been minimized.

Copy link
Member

@rliesenfeld rliesenfeld commented Jul 14, 2014

Yes, it makes more intuitive sense, except that partial mocking is applied later during the execution of the test, so pre-JMockit 1.9 it would always override the @Injectable mocking.

I am considering changing the semantics in cases like this, so that instance methods already mocked through an @Injectable are disregarded if the same class is partially mocked later in the test. But it needs more thinking; an idea that occurs is that the use of a "MockUp" may be a better choice here.

@rliesenfeld rliesenfeld self-assigned this Jul 15, 2014
@rliesenfeld

This comment has been minimized.

Copy link
Member

@rliesenfeld rliesenfeld commented Jul 15, 2014

The current semantics will be changed so that @Injectable instances are unaffected by an eventual partial mocking of the same class.

rliesenfeld added a commit that referenced this issue Jul 20, 2014
… unaffected, if there are any of the same class. Fixes issue #1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants
You can’t perform that action at this time.