Mockito versus EasyMock

In this recipe, we will write a simple test using EasyMock that verifies the behavior of the system under test when an exception is thrown.

Getting ready

In order to profit from EasyMock, you need to add it to your classpath. This is the configuration for Gradle:

testCompile 'org.easymock:easymock:3.2'

The following is how you add the EasyMock dependency in Maven:

<dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymock</artifactId>
    <version>3.2</version>
    <scope>test</scope>
</dependency>            

Let's assume that our system under test is the tax transferring system for a given person, as shown in the following code:

public class TaxTransferer {

  private final TaxService taxService;

  public TaxTransferer(TaxService taxService) {
    this.taxService = taxService;
  }

  public boolean transferTaxFor(Person person) {
    if (taxService.hasAlreadyTransferredTax(person)) {
      return false;
    }
    try {
      taxService.transferTaxFor(person);
    } catch (Exception exception) {
      System.out.printf("Exception [%s] caught while trying totransfer tax for [%s]%n", exception, person.getName());
      return false;
    }
    return true;
  }

}

How to do it...

In order to test the system using EasyMock, you need to perform the following steps:

  1. Record the mock's behavior (tell the mock how it should behave).
  2. Replay the mock's behavior (stops recording).
  3. Execute the logic of the system under test.
  4. Verify the behavior of the system under test.

The following is an example of a JUnit test with EasyMock:

@RunWith(EasyMockRunner.class)
public class TaxTransfererTest {

  @Mock TaxService taxService;

  TaxTransferer systemUnderTest;  
  
    @Test
    public void should_return_false_when_tax_was_not_transfered_and_connection_to_irs_was_refused() {
        // expect
      systemUnderTest =  new TaxTransferer(taxService);	    
        Person person = new Person();
      expect(taxService.hasAlreadyTransferredTax(anyObject(Person.class))).andReturn(false);
        taxService.transferTaxFor(same(person));
        expectLastCall().andStubThrow(new RuntimeException("Connection refused"));
        replay(taxService);

        // act
        boolean transferSuccessful = systemUnderTest.transferTaxFor(person);

        // assert
        then(transferSuccessful).isFalse();
	    verify(taxService);
    }

}

Note

EasyMock integrates very nicely with JUnit. You need to annotate your test class with @RunWith(EasyMockRunner.class). Only then can you profit from the @Mock annotation that will create the mock for you; @TestSubject will inject proper mocks for you. Unfortunately, as you can see in our example, our system under test wasn't annotated with @TestSubject. That's because TaxTransferer fields are final and we inject their collaborators via constructor. EasyMock doesn't support constructor injection, it only supports field injection. This is why we need to inject the collaborator manually.

The following is how you can integrate EasyMock with TestNG:

public class TaxTransfererTestNgTest {

    @Mock TaxService taxService;

    TaxTransferer systemUnderTest;

    @BeforeMethod
    public void setup() {
        EasyMockSupport.injectMocks(this);
	    systemUnderTest = new TaxTransferer(taxService);
    }

    @Test
    public void should_return_false_when_tax_was_not_transfered_and_connection_to_irs_was_refused() {
        // expect
	    Person person = new Person();
	    expect(taxService.hasAlreadyTransferredTax(anyObject(Person.class))).andReturn(false);
	    taxService.transferTaxFor(same(person));
	    expectLastCall().andStubThrow(new RuntimeException("Connection refused"));
	    replay(taxService);
	    // act
	    boolean transferSuccessful = systemUnderTest.transferTaxFor(person);

	    // assert
	    then(transferSuccessful).isFalse();
	    verify(taxService);
    }

}

How it works...

We will not discuss how EasyMock works internally, but focus on what happens in the test itself, and what the EasyMock approach is. EasyMock's approach towards mocks is captured in the following steps:

  1. You need to record the mock's behavior, that is, teach it how it should react. By default, EasyMock creates strict mocks so their default behavior is such that if a method on a mock was called and you didn't expect it, then your test will fail.
  2. Once you're done, you need to replay the mock (stop recording by calling the replay(T mock) static method). Afterwards, you can act and assert the results.
  3. Finally, you can verify the mock's behavior. Verifying means EasyMock will check whether the methods you expected were actually called as many times as you defined in the record section.

As for stubbing, you can call the static expect(T mock) method to stub a method execution that returns a value. To stub void methods, you need to first execute the void method and then call the static expectLastCall() method. Only then can you define exactly how the mock should behave.

There's more...

Mockito's test code of the system would look like the following (example for JUnit):

@RunWith(MockitoJUnitRunner.class)
public class TaxTransfererTest {

    @Mock TaxService taxService;

    @InjectMocks TaxTransferer systemUnderTest;

    @Test
    public void should_return_false_when_tax_was_not_transfered_and_connection_to_irs_was_refused() {
        // given
        Person person = new Person();
      given(taxService.hasAlreadyTransferredTax(person)).willReturn(false);
        willThrow(new TaxServiceConnectionException("Connection refused")).given(taxService).transferTaxFor(person);

        // when
        boolean transferSuccessful = systemUnderTest.transferTaxFor(person);

        // then
        then(transferSuccessful).isFalse();
        verify(taxService).hasAlreadyTransferredTax(person);
        verify(taxService).transferTaxFor(person);
    }

}

The primary similarities between EasyMock and Mockito are as follows:

  • The same level of verification (in terms of unexpected, redundant invocations, and verification in order)
  • Similar approach to argument matching (like same(...), anyObject(...), and so on)

The primary differences between EasyMock and Mockito are as follows:

  • Mockito doesn't have the record replay mode since it can only stub or verify mocks. The former happens before execution and the latter after the execution.
  • By default, Mockito creates "nice" mocks, thus if not stubbed, mocks will return a set of default values. In EasyMock, you need to create such a mock explicitly because all mocks are strict by default.
  • Verification in Mockito is optional. In EasyMock, you would need to create a nice mock and then not call the verify() method.
  • Mockito's custom argument matchers use Hamcrest matchers so you can reuse them in different parts of the application.
  • EasyMock is a better tool for verification in order than Mockito. Let's assume that we have a method that is executed twice and mutates the input parameter like in the following pseudo code:
    collaborator.execute(mutate(object));
    collaborator.execute(mutate(object));

In EasyMock, since you define expectations at the beginning, you are able to verify the value of the argument of the execute(…) method at each step. In Mockito, you will only be able to check that at the second execution, thus having inOrder.verify(mockedCollaborator).execute(objectAtStep1()); and inOrder.verify(mockedCollaborator).execute(objectAtStep2()); would make only the second line pass whereas the first would fail. EasyMock's way to test it would be as follows:

    mockedCollaborator.execute(mutate(object));
    mockedCollaborator.execute(mutate(object));
    replay(mockedCollaborator);

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.188.121