0

Given interface:

public interface Calculator<T extends Integer>
{
    void calculate(T a, T b);
}

classes which implement this interface:

public class CalculatorAddition implements Calculator
{
    public void calculate(Integer a, Integer b)
    {
        System.out.println(a + b);
    }
}

public class CalculatorSubtraction implements Calculator
{
    public void calculate(Integer a, Integer b)
    {
        System.out.println(a - b);
    }
}

and classes that calculate addition and subtraction in the following way:

public class CalculatorFlow
{
    public void compute(Calculator calc, Integer a, Integer b)
    {
        calc.calculate(a, b);
    }
}

public class CalculatorFlowCalculations
{
    private CalculatorFlow calculatorFlow = new CalculatorFlow();
    private CalculatorAddition calculatorAddition = new CalculatorAddition();
    private CalculatorSubtraction calculatorSubtraction = new CalculatorSubtraction();

    public void doCalculations()
    {
        int a = 5;
        int b = 5;

        calculatorFlow.compute(calculatorAddition, a, b);
        calculatorFlow.compute(calculatorSubtraction, a , b);
    }
}

I'd like to check if the compute method from CalculatorFlow was executed first with CalculatorAddition object and then with CalculatorSubtraction object. Note that compute method takes them as Calculator interface reference. When I do it like that:

@RunWith(MockitoJUnitRunner.class)
public class CalculatorFlowTest
{
    @Mock
    private CalculatorFlow calculatorFlow;

    @InjectMocks
    private CalculatorFlowCalculations calculatorFlowCalculations = new CalculatorFlowCalculations();

    @Test
    public void testDoCalculations()
    {
        InOrder inOrder = inOrder(calculatorFlow);

        calculatorFlowCalculations.doCalculations();

        inOrder.verify(calculatorFlow).compute(any(CalculatorAddition.class), anyInt(), anyInt());
        inOrder.verify(calculatorFlow).compute(any(CalculatorSubtraction.class), anyInt(), anyInt());
    }
}

I get:

org.mockito.exceptions.verification.VerificationInOrderFailure: 
Verification in order failure:
calculatorFlow.compute(<any>, <any>, <any>);
Wanted 1 time:
-> at CalculatorFlowTest.testCalc(CalculatorFlowTest.java:27)
But was 2 times. Undesired invocation:
-> at CalculatorFlowCalculations.doCalculations(CalculatorFlowCalculations.java:13)

As I can understand, the method is called twice because of interface type reference in compute method, so my question is: how to check in that case if the method executes in particular order with specific arguments?

Sebastian
  • 23
  • 6
  • 2
    If you want to use mocks in the test, you should somehow make it possible to inject them into `CalculatorFlowCalculations` (e.g. through constructor injection). That would be the first step. Reading through a tutorial like [this one](https://dzone.com/articles/getting-started-mocking-java) would also help. – Mick Mnemonic May 15 '17 at 18:25
  • Isn't it done by `@InjectMocks` annotation? That part actually looks like it is working. – Sebastian May 15 '17 at 18:31
  • are CalculatorAddition and CalculatorSubtraction spring beans? If so , then why are creating this object as new (...). If not how will InjectMocks work? – pvpkiran May 15 '17 at 18:32
  • @Sebastian, true, `@InjectMocks` will inject the mocks (through bytecode manipulation), so you can use it for classes that don't support normal dependency injection (I hadn't used that annotation before). However, you are currently not using these properly, but instead creating real objects into these fields in the test class. Have a look at [this question](http://stackoverflow.com/questions/16467685/difference-between-mock-and-injectmocks) for the correct syntax (you shouldn't initialize the fields as Mockito will do it for you). – Mick Mnemonic May 15 '17 at 18:38
  • Don't use raw types. `Integer` is a `final` class, so `Calculator` makes no sense. Since your generics parameter allows _only_ `Integer`, the type shouldn't even be generic in the first place. Eliminate the generics and raw types won't be an issue. Use `@Override` every override, every time. – Lew Bloch May 15 '17 at 18:58
  • "method is called twice because of interface type reference in compute method" What do you mean by that? Use of an interface does not _per se_ cause multiple method invocations. – Lew Bloch May 15 '17 at 19:00
  • @pvpkiran No, these are not beans. @LewBloch You are absolutely right that it makes no sense, it was done just to "mute" the compilator while performing some checks. I mean that the method is called with `CalculatorAddition` and `CalculatorSubtraction` twice, using `Calculator` reference. – Sebastian May 15 '17 at 19:16

1 Answers1

0

I achieved described check using ArgumentCaptor:

@Captor
ArgumentCaptor<Calculator> captor;

@Test
public void testDoCalculations()
{
    InOrder inOrder = inOrder(calculatorFlow);

    calculatorFlowCalculations.doCalculations();

    inOrder.verify(calculatorFlow, times(2)).compute(captor.capture(), anyInt(), anyInt());
    assertEquals(CalculatorAddition.class, captor.getAllValues().get(0).getClass());
    assertEquals(CalculatorSubtraction.class, captor.getAllValues().get(1).getClass());
}

Using the above syntax I can check both the order of execution and type of object passed as argument.

Thanks for all comments, they helped me a lot in finding the answer.

Sebastian
  • 23
  • 6