How do Java mocking frameworks work?

前端 未结 5 456
攒了一身酷
攒了一身酷 2021-02-03 23:10

This is NOT a question about which is the best framework, etc.

I have never used a mocking framework and I\'m a bit puzzled by the idea. How does it know how to create t

相关标签:
5条回答
  • 2021-02-03 23:50

    I will speak of the framework that I use ( jmock ), but others do something very similar.

    How does it know how to create the mock object?

    It does not, you tell the framework that you require a mock object and you give it a type ( ideally an interface ) of the object that you would like to mock. Behind the scenes the mock framework uses a combination of reflection and, sometimes, byte-code rewrites to create a mock object.

    Is it done in runtime or generates a file?

    jMock creates it at runtime.

    How do you know its behavior?

    Mock objects are pretty dumb. You specify the behavior that you expect of them.

    And most importantly - what is the work flow of using such a framework (what is the step-by-step for creating a test).

    One very important thing that framework must provide is an ability to check that expected behavior has been observed during the test execution.

    jmock does it by introducing specific test runner that checks that all expectations on all declared mock objects after the test has finished. If something does not match, the exception is thrown.

    Usually the pattern is:

    1. Acquire mock factory ( called Mockery in jmock )out of thin air ( with specific no-args constructor ).
    2. Create required mock objects.
    3. Setup expectation on mock objects
    4. Call the method you are testing, passing mocks instead of real objects
    5. If your method returns some values, check expected value with regular assertXXX methods.
    0 讨论(0)
  • 2021-02-04 00:05

    I think that EasyMock's documentation is a get way to get you started. It features a lot of examples that are easy to grasp.

    0 讨论(0)
  • 2021-02-04 00:12

    EasyMock (http://easymock.org/) is the mocking library that I have used the most. (There are others: jMock, Mockito, etc.)

    Most of these create an object at runtime, that you can control. The basic flow is:

    1. Create a mock
    2. Specify how it should act by telling the mock framework what calls to expect. (This varies based on the framework.)
    3. Use the mock in your test as if it were the real thing
    4. Some frameworks allow (or require) that you "verify" that the mock was used correctly.
    0 讨论(0)
  • 2021-02-04 00:13

    A mocking framework takes the redundancy and boilerplate out of a mocking test.

    It knows to create the Mock object because you tell it to, of course (unless you meant something else with that question).

    The "standard" way of creating a mock object is to use/abuse the java.lang.reflect.Proxy class to create a runtime implementation of the interface. This is done at runtime. Proxy has a limitation in that it cannot proxy concrete classes. To accomplish mocking of concrete classes requires dynamic bytecode creation that creates subclasses that override the real implementation of the public methods with essentially what would be done with a Proxy (record the method parameters and return a pre-determined value). This has a limitation in that it cannot subclass final classes. For that, you have solutions like JDave, which (I believe, I didn't confirm this) muck with the classloader to remove the final designation on the class before loading it, so the class isn't in fact final at runtime as far as the JVM is concerned.

    The Mocking framework is basically all about capturing the parameters and verifying them against pre-determined expectations, and then returning a pre-configured or reasonable default value. It doesn't behave in any particular way, which is the point. The calling code is being verified that it calls the method with the correct parameter, and perhaps how it reacts to a specific return values or thrown exceptions. Any side effects or real accomplishments of the call on the real object do not happen.

    Here is a real example from a project, using JMock with JUnit4. I have added comments to explain what is going on.

     @RunWith(JMock.class) //The JMock Runner automatically checks that the expectations of the mock were actually run at the end of the test so that you don't have to do it with a line of code in every test.
    public class SecuredPresentationMapTest {
    
    private Mockery context = new JUnit4Mockery(); //A Mockery holds the state about all of the Mocks. The JUnit4Mockery ensures that a failed mock throws the same Error as any other JUnit failure.
    
    @Test
    public void testBasicMap() {
        final IPermissionsLookup lookup = context.mock(IPermissionsLookup.class); //Creating a mock for the interface IPermissionsLookup.
        context.checking(new Expectations(){{ //JMock uses some innovative but weird double brace initialization as its standard idom.
            oneOf(lookup).canRead(SecuredEntity.ACCOUNTING_CONTRACT);
                //expect exactly one call to the IPermissionsLookup.canRead method with the the enum of ACCOUNTING_CONTRACT as the value. Failure to call the method at all causes the test to fail.
                will(returnValue(true)); //when the previous method is called correctly, return true;
        }});
    
        Map<String, Component> map = new SecuredPresentationMap(lookup, SecuredEntity.ACCOUNTING_CONTRACT);
        //This creates the real object under test, but passes a mock lookup rather than the real implementation.
        JLabel value = new JLabel();
        map.put("", value);
        assertThat(((JLabel) map.get("")), is(value)); //This ensures that the Map returns the value placed, which it should based on the fact that the mock returned true to the security check.
      }
    }
    

    If the mock passed in was ignored, the test would have failed. If the map fails to return the value placed in it the test fails (that is standard JUnit).

    What is being tested here and in another opposite test is that depending on what the IPermissionsLookup interface says about the security the Map changes its behavior about what is returned. This is the base good case. The other test, the mock returns false and something else is expected from the Map. Using the mock ensures that the map relies on the IPermissionsLookup method to determine the security posture and what is being returned.

    0 讨论(0)
  • 2021-02-04 00:14

    For a better understanding of mocking, you might like to make your own mock object. It's a pretty simple process - you make a class which implement the same interface as what you're mocking, and give it the behaviour you require, either recording calls to a method, or responding in a set way when called. From there the way that mocking frameworks are set up begins to make more sense.

    0 讨论(0)
提交回复
热议问题