How should I use EasyMock's @Mock annotation (

2019-06-03 22:22发布

问题:

It looks like EasyMock version 3.2 now supports using annotations to setup mock objects. I am new to EasyMock (and Java in general) and am trying to understand how to use this. Do these annotations do something new or just provide an alternative way to do things? The documentation says:

Since EasyMock 3.2, it is now possible to create mocks using annotations. This is a nice and shorter way to create your mocks and inject them to the tested class. Here is the example above, now using annotations: ...

Then there is a listing that shows use of the @TestSubject and @Mock annotations, but I don't understand how it works. It seems as if it magically sets the private field of the class under test to the mock object. In most of my cases, I just want to make mock objects that return pre-defined values for use in JUnit test cases (don't currently care about verifying which ones were called, how many times they were called, etc). For example, for some tests I want to create a fake HttpServletRequest object like this:

public class SomeTest {
    // Construct mock object for typical HTTP request for the URL below 
    private static final String REQUEST_URL = "http://www.example.com/path/to/file?query=1&b=2#some-fragment";
    private static final Map<String, String> requestHeaderMap;
    static {
        Map<String, String> requestHeaders = new LinkedHashMap<String, String>();
        requestHeaders.put("host", "www.example.com");
        // ... (add any other desired headers here) ...
        requestHeaderMap = Collections.unmodifiableMap(requestHeaders);
    }

    private HttpServletRequest httpServletRequest;
    // ...

    @Before
    public void setUp() throws Exception {            
        httpServletRequest = createNiceMock(HttpServletRequest.class);
        expect(httpServletRequest.getRequestURI()).andReturn(REQUEST_URL).anyTimes();
        expect(httpServletRequest.getHeaderNames()).andReturn(Collections.enumeration(requestHeaderMap.keySet())).anyTimes();
        capturedString = new Capture<String>();
        expect(httpServletRequest.getHeader(capture(capturedString))).andAnswer(new IAnswer<String>() {
            public String answer() throws Throwable {
                String headerName = capturedString.getValue().toLowerCase();
                if (requestHeaderMap.containsKey(headerName))
                    return requestHeaderMap.get(headerName);
                else 
                    return "";
            }
        }).anyTimes();

        replay(httpServletRequest);

        // ...
    }

    @Test
    public void someMethod_givenAnHttpServletRequest_shouldDoSomething() {
        // ...
    }
}

Could I change the above code to use annotations? If so, should I? Under what circumstances?

I thought perhaps putting the @Mock annotation above an instance variable declaration would automatically take care of the createNiceMock(...) part, but this does not seem to work, so I suspect that I am misunderstanding something.

回答1:

Examining their source code, they are using reflection to inject anything with an @Mock into a field of the @TestSubject. Their javadoc for the method

public static void injectMocks(final Object obj)

in EasyMockSupport.java says:

Inject a mock to every fields annotated with {@link Mock} on the class passed in parameter. Then, inject these mocks to the fields of every class annotated with TestSubject.

The rules are

  • Static and final fields are ignored
  • If a mock can be assigned to a field, do it. The same mock an be assigned more than once
  • If no mock can be assigned to a field, skip it silently
  • If two mocks can be assigned to the same field, return an error
  • Fields are searched recursively on the superclasses

Note: If the parameter extends EasyMockSupport, the mocks will be created using it to allow replayAll/verifyAll to work afterwards

@param obj the object on which to inject mocks

@since 3.2

public static void injectMocks(final Object obj) { ... }

For you to use the @Mock annotation, you would need a @TestSubject that has an HttpServletRequest field for EasyMock to set the @Mock on (via reflection). The annotations are provided to make it a little easier to wire up a test, it let's you skip the createMock, and then calling the settter yourself.