Mockito - Spy vs. Layout

Mockito - I understand that a spy calls real methods on an object, and the layout calls methods on a double object. Spyware should also be avoided if there is no code smell. However, how do spies work, and when should I use them? How do they differ from ridicule?

+44
java unit-testing mockito mocking spy
Feb 03 '15 at 9:38
source share
6 answers

Technically speaking, “bullying” and “spies” are a special type of “test doubling”.

Mockito, unfortunately, makes the distinction weird.

The layout in mockito is a normal layout in other mocking frameworks (it allows you to mute calls, that is, return certain values ​​from method calls).

The spy in mockito is a partial mock in other mocking frameworks (part of the object will be mocked, and some will use real method calls).

+50
Feb 03 '15 at 16:20
source share

I created a sample example here https://www.surasint.com/mockito-with-spy/

I copy some of them here.

If you have something like this code:

public void transfer( DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, double amount, String fromAccount, String toAccount) { withdrawMoneyService.withdraw(fromAccount,amount); depositMoneyService.deposit(toAccount,amount); } 

You may not need a spy, because you can just mimic DepositMoneyService and WithdrawMoneyService.

But with some inherited code, the dependency is in the code:

  public void transfer(String fromAccount, String toAccount, double amount) { this.depositeMoneyService = new DepositMoneyService(); this.withdrawMoneyService = new WithdrawMoneyService(); withdrawMoneyService.withdraw(fromAccount,amount); depositeMoneyService.deposit(toAccount,amount); } 

Yes, you can change the first code, but then the API will change. If this method is used in many places, you must change all of them.

An alternative is that you can extract the dependency as follows:

  public void transfer(String fromAccount, String toAccount, double amount){ this.depositeMoneyService = proxyDepositMoneyServiceCreator(); this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator(); withdrawMoneyService.withdraw(fromAccount,amount); depositeMoneyService.deposit(toAccount,amount); } DepositMoneyService proxyDepositMoneyServiceCreator() { return new DepositMoneyService(); } WithdrawMoneyService proxyWithdrawMoneyServiceCreator() { return new WithdrawMoneyService(); } 

Then you can use the spy to inject dependency as follows:

 DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class); WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class); TransferMoneyService target = spy(new TransferMoneyService()); doReturn(mockDepositMoneyService) .when(target) .proxyDepositMoneyServiceCreator(); doReturn(mockWithdrawMoneyService) .when(target) .proxyWithdrawMoneyServiceCreator(); 

More details in the link above.

+10
Apr 02 '17 at 19:06 on
source share

TL; DR version

With mock, it creates a bare-bone shell instance for you.

 List<String> mockList = Mockito.mock(ArrayList.class); 

With a spy, you can partially mock an existing instance

 List<String> spyList = Mockito.spy(new ArrayList<String>()); 

A typical use case for Spy: the class has a parameterized constructor; you want to create an object first.

+8
Aug 28 '17 at 19:41
source share

The best place to run is probably the docs for mockito .

In general, mockito mock allows you to create stubs.

You will create a stub method if, for example, this method performs an expensive operation. Say it receives a connection to a database, retrieves a value from the database, and returns it to the caller. Getting a db connection may take 30 seconds, which will slow down the test until you probably switch to context (or stop the test).

If the logic you are testing does not care about connecting to the database, you can replace this method with a stub that returns a hard coded value.

The mockito spy allows you to check whether a method calls other methods. This can be very useful when trying to test outdated code.

It is useful if you are testing a method that works through side effects, then you will use the spy from mockito. These delegates access the real object and allow you to check the method call, the number of times it is called, etc.

+7
Feb 03 '15 at 10:06
source share

Both can be used to mock methods or fields. The difference is that in the layout you create a full layout or a fake object during a spy, there is a real object, and you just spy on or stumble on it certain methods.

Although in spy objects, of course, since this is a real method, when you do not execute the method, then it will call the behavior of the real method. If you want to change and make fun of a method, you need to drown it out.

Consider the example below as a comparison.

 import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MockSpy {    @Mock    private List<String> mockList;    @Spy    private List<String> spyList = new ArrayList();    @Test    public void testMockList() {        //by default, calling the methods of mock object will do nothing        mockList.add("test"); Mockito.verify(mockList).add("test"); assertEquals(0, mockList.size());        assertNull(mockList.get(0));    }    @Test    public void testSpyList() {        //spy object will call the real method when not stub        spyList.add("test"); Mockito.verify(spyList).add("test"); assertEquals(1, spyList.size());        assertEquals("test", spyList.get(0));    }    @Test    public void testMockWithStub() {        //try stubbing a method        String expected = "Mock 100";        when(mockList.get(100)).thenReturn(expected);        assertEquals(expected, mockList.get(100));    }    @Test    public void testSpyWithStub() {        //stubbing a spy method will result the same as the mock object        String expected = "Spy 100";        //take note of using doReturn instead of when        doReturn(expected).when(spyList).get(100);        assertEquals(expected, spyList.get(100));    } } 

When do you use a layout or spy? If you want to be safe and avoid calling external services and just want to test the logic inside the device, use the layout. If you want to call an external service and make a real dependency call, or just say that you want to run the program as it is, and just drown out specific methods, then use a spy. So the difference between a spy and a layout in mockito.

+6
Dec 19 '17 at 15:22
source share

I like the simplicity of this recommendation:

  • If you want to be safe and avoid calling external services and just want to test the logic inside the device, use the layout .
  • If you want to call an external service and make a call of real dependencies, or just say that you want to run the program as is, and just drown out the specific methods, and then use a spy .

Source: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/

0
Dec 05 '18 at 0:10
source share



All Articles