My First Post      My Facebook Profile      My MeOnShow Profile      W3LC Facebook Page      Learners Consortium Group      Job Portal      Shopping @Yeyhi.com









Thursday, December 3, 2020

Mockito Vs EasyMock - How to use and the difference in Unit Testing

What is mocking? 

A unit test should be independent of any external resources : database, message queue etc.

Ideally, a unit test should test only one class. Mocking is a best practice when testing an object which is linked to other objects.

Example : CompanyCreateTest should only test CompanyCreate


Mockito and EasyMock:


Both are java mocking libraries for Unit Testing, and follow the same model of

  1. Mock external dependencies
  2. Setup expectations
  3. Run test
  4. Verify results

Both of the above are equivalent in features and capabilities. The essential difference exists in their usage at point 3 and 4. EasyMock needs explicit replaying and verification of Mocks and Mockito doesn't. 

Similarities :

  • Mocks concrete classes as well as interfaces
  • Supports exact-number-of-times and at-least-once verification
  • Argument matchers
  • Annotation support

Remember that for Mocking, we will use mocks in our object. So, if you are using interfaces and IOC you can write your own fake implementations and inject it in your tested object. However, writing fake implementations for every linked objects could be very fastidious. That's why we will use mock libraries.


Differences between both:

Mockito has a simplified api that results in much flatter learning curve and ease of writing and maintainability. A great benefit outlined above is the developer doesn't need to call out what mocks need to be replayed. This is handled implicitly in Mockito. That compared to Easy Mock, the learning curve is higher and results in confusion several times when new mocks are added/deleted, all the explicit references need to be accordingly handled. EasyMock results in more code than Mockito to test the same functionality.

 

A simplified EasyMock example below :

TestClassA {
 
void setup(){
//Create mock
ClassBeingMocked mock1 = EasyMock.createMock(ClassBeingMocked);
}
 
 
void testMethod(){
//Setup expectations
EasyMock.expect(mock1.getAge()).andReturn(10);
//Replay mode
EasyMock.replay(mock1);
//Test
ClassBeingTested source = new ClassBeingTested();
source.calculateAge(mock1);
//Assertions
assertEquals(50, source.getAverageAge());
//Verification mode
EasyMock.verify(mock1);
}
}

Same example in Mockito

TestClassA {
 
 
void setup(){
//Create mock
ClassBeingMocked mock1 = Mockito.Mock(ClassBeingMocked);
}
 
 
void testMethod(){
//Setup expectations
Mockito.when(mock1.getAge()).thenReturn(10);
 
//Test
ClassBeingTested source = new ClassBeingTested();
source.calculateAge(mock1);
 
//Assertions
assertEquals(50, source.getAverageAge());
 
//Verification mode
Mockito.verify(source).someInternalMethodCalled();
}
}

Using Mockito:

Definition

Mockito is a Java library used to mock objects.

Basically, you can write the following to create a fake implementation of a List :

List mockedList = mock(List.class);

And you can specify the expected behaviour of your mock for your test case :

when(mockedList.get(0)).thenReturn("first");

MockitoJUnitRunner

Mockito comes with a JunitRunner you could enable like that :

@RunWith(MockitoJUnitRunner.class)
public class TestDefaultRule
{
    @InjectMocks
    private DefaultRule defaultRule = new DefaultRule();
     
    @Mock
    private AirProductManager apManager;
     
    @Mock
    private AirProduct airProduct ;
...
}

Doing that, Mockito will create all your mocks for every objects annotated with @Mock and inject those mocks in DefaultRule that was annotated with @InjectMocks.

Inject mock without setters

The annotation @InjectMocks is very usefull when your object do not expose setter as for the object DefaultRule :

public class DefaultRule implements MarkupRule {
...
    @Autowired
    private AirProductManager airProductManager;
...

airProductManager is private and has no setters, so without the annotation @InjectMocks you would have used very tricky method to inject your mock manually.

Initialize value without setters

Sometimes, you could have some objects initialized by @Value annotation and without setters.
Exemple in TneManager :

public class TneManagerImpl implements TneManager
{
    @Value("${DimoAuthentificationCryptingStrategy.PassKey}")
    private String dimoPassKey;
    @Value("${EolAuthentificationCryptingStrategy.PassKey}")
    private String eolPassKey;
...
}

In this situation, you have a solution using ReflectionTestUtils from Spring in your setup method :

@Before
public void initialize ()
{
    ReflectionTestUtils.setField(tne, "dimoPassKey""dimo");
    ReflectionTestUtils.setField(tne, "eolPassKey""eol");
}

Mock static method

Mockito is not able to mock static method by itself. However when using static method you can mix Mockito and PowerMock.

To enable PowerMock you have to use the runner PowerMockRunner.

Example in AirThirdPartyLinkImplMockitoTest :

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Lookup.class })
@PowerMockIgnore("org.apache.commons.logging.*")
public class AirThirdPartyLinkImplMockitoTest
{
...
    @Before
    public void initialize() throws Exception
    {
        // As you don't use MockitoRunner, you have to initialize all mocks with the following line
        MockitoAnnotations.initMocks(this);
...
    // o use PowerMockito to mock the static call of Lookup.getInstance()
    // A lot of stuff just to use Carbon...
    // -------------------------------------------------------------------
    PowerMockito.mockStatic(Lookup.class);
        Lookup lookup = mock(Lookup.class);
        when(Lookup.getInstance()).thenReturn(lookup);
        PNRConstructManager mgr = mock(PNRConstructManager.class, withSettings().extraInterfaces(Component.class));
        when(lookup.fetchComponent(PNRConstructManager.DEFAULT_COMPONENT_PATH)).thenReturn((Component)mgr);
 
    }

Explanations :

Enable the runner :

@RunWith(PowerMockRunner.class)

Explicitely tell which object will be mocked

@PrepareForTest({ Lookup.class })

The following line is needed in the setup method. Otherwise every annotations Mock and InjectMock will be ignored as you don't use MockitoJUnitRunner

MockitoAnnotations.initMocks(this);

and the mock of the static method :

PowerMockito.mockStatic(Lookup.class);
    Lookup lookup = mock(Lookup.class);
    when(Lookup.getInstance()).thenReturn(lookup );

No comments:

Post a Comment