For what reason should I mock?

2019-06-20 05:24发布

问题:

I am new to Mockito and PowerMockito as well. I found out that I can not test static methods with pure Mockito so I need to user PowerMockito (right?).

I have very simple class called Validate with this very easy method

public class Validate {
        public final static void stateNotNull(
            final Object object,
            final String message) {
    if (message == null) {
        throw new IllegalArgumentException("Exception message is a null object!");
    }
    if (object == null) {
        throw new IllegalStateException(message);
    }
}

So I need to verify that:

1) When I call that static method on null message argument, IllegalArgumentException is called
2) When I call that static method on null object argument, IllegalStateException is called

From what I got so far, I wrote this test:

import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.isNull;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.testng.annotations.Test;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Validate.class)
public class ValidateTestCase {

    @Test(expectedExceptions = { IllegalStateException.class })
    public void stateNotNullTest() throws Exception {
        PowerMockito.mockStatic(Validate.class);
        Validate mock = PowerMockito.mock(Validate.class);
        PowerMockito.doThrow(new IllegalStateException())
            .when(mock)
            .stateNotNull(isNull(), anyString());
        Validate.stateNotNull(null, null);
    }
}

So this says that I mock Validate class and I am checking that when mock is called on that method with null argument as an object and any string as a message, an IllegalStateException is thrown.

Now, I really don't get it. Why I just can't call that method directly, dropping the whole voodoo magic around mocking that static class? It seems to me that unless I call Validate.stateNotNull that test passes anyway ... For what reason should I mock it?

回答1:

First, decide what your objective is and what you want to test. Your test isn't testing your Validate class method, it's creating a mock that behaves like that method, as Fortega points out. Identify what it is you are testing (the object under test) and what it is you need in order to perform the test (the collaborators), then look at the collaborators and decide whether they are things that are easy to create or if you need to mock them.

For something like this class which has no dependencies on anything, I would recommend doing without mocks entirely. There is nothing here that needs mocking, the test can be written like this:

import static org.junit.Assert.*;

public class ValidateTestCase {

    @Test
    public void testHappyPath() throws Exception {
        Validate.stateNotNull("", "");
    }

    @Test
    public void testNullMessage() throws Exception {
        try {
            Validate.stateNotNull(null, null);
            fail();
        }
        catch (IllegalStateException e) {
            String expected = "Exception message is a null object!"
            assertEquals(expected, e.getMessage());
        }
    }

    @Test(expected=IllegalStateException.class)
    public void testNullObject() throws Exception {
        Validate.stateNotNull(null, "test");
    }
}

and that tells you whether the code does what you want it to.

Don't mock unless there is some dependency that you want to avoid introducing to the test due to it being either an external resource (like a filesystem or database) or some complex subsystem. The mock frameworks can be very useful but they add complexity, they can over-specify the behavior of the things they are testing, making the tests brittle, and they can make tests hard to read. Do without them if you can.



回答2:

You should not mock the classes and methods you are testing. You should only mock methods that are needed to perform the test itself.

For example, if you need some objects from a web service to perform a test, you could mock the web service calls, so you don't need to actually call the web service.