1

**UPDATE : I have misunderstood the way spys function completely, I should be calling the spy version of a method in order for it to be verified NOT the real object method Ex : r.getPrice(); then verify(r).getPrice(); I still haven't figured the right way to get what i want but i thought i have to ask if i should delete this question ?

I'm spying an array element like this

@Test
public void testMakeCoffee_1() {
    Recipe r = spy(stubRecipies[0]);

    assertEquals(25,coffeeMaker.makeCoffee(0, 75)); // first index

            verify(r).getPrice();
});

this is the implementation of the makeCoffee method

public synchronized int makeCoffee(int recipeToPurchase, int amtPaid) {
    int change = 0;

    if (getRecipes()[recipeToPurchase] == null) {
        change = amtPaid;
    } else if (getRecipes()[recipeToPurchase].getPrice() <= amtPaid) {
        if (inventory.useIngredients(getRecipes()[recipeToPurchase])) {
            change = amtPaid - getRecipes()[recipeToPurchase].getPrice();
        } else {
            change = amtPaid;
        }
    } else {
        change = amtPaid;
    }

    return change;
}

getRecipes() implementation inside CoffeeMaker class

public synchronized Recipe[] getRecipes() {
    return recipeBook.getRecipes();
}

RecipeBook is a mocked class, stubRecipies is an array that contains my custom recipes to test and getRecipes() of the RecipeBook class is stubbed like this

recipeBookStub = mock(RecipeBook.class);
stubRecipies = new Recipe [] {recipe1, recipe2, recipe3};
when(recipeBookStub.getRecipes()).thenReturn(stubRecipies);

getRecipes() should return the list of recipes, hence replacing it with my array of recipes stubRecipies.

however when i call verify method of mockito on the getPrice() method of the spy object stubRecipies[0] i get a 'wanted but not invoked error' how is it possible that the method is not invoked, knowing that the usage above returns the correct value.

** edit : i tried manually calling recipe1.getPrice() and still i got 'wanted but not invoked error' , but when i called r.getPrice() the test passed which is weird because i thought a spy object is supposed to capture the interactions with the real object.

A.Mahmoud
  • 65
  • 7
  • hello, we may need a little more code to be sure of the cause. – MartinByers Jun 02 '20 at 22:06
  • hello Martin, at first i wanted my question to look simple because i suspected my issue had something to do with array elements , I followed your advice in case i'm missing something elsewhere and to make the question more clear, thank you for taking the time – A.Mahmoud Jun 02 '20 at 22:21
  • No problem, coffeeMaker calls getRecipes(), how does this relate to recipeBookStub? – MartinByers Jun 02 '20 at 22:25
  • sorry i missed adding the getRecipes() implementation which invokes getRecipes() from the RecipeBook that is the mocked class i was referring to, I edited the question – A.Mahmoud Jun 02 '20 at 22:36
  • How are you mocking recipeBookStub, and how are you injecting the mocked recipeBookStub into CoffeeMaker ? – MartinByers Jun 02 '20 at 22:39
  • thank you for your helpful directions, i added the mocking instruction to the question and i'm not injecting the mocked recipeBookStub into CoffeeMaker i only need the getRecipes() so i stubbed it using when(recipeBookStub.getRecipes()).thenReturn(stubRecipies); – A.Mahmoud Jun 02 '20 at 23:04

1 Answers1

1

So from looking at your question and the comments, there are a few things I would recommend to take this forward.

So without seeing the entire code, I can not 100% sure if this is returning the expected Recipe[]

public synchronized Recipe[] getRecipes() {
    return recipeBook.getRecipes();
}

You should be injecting your mocked recipeBookStub into your CoffeeMaker.

Typically when writing code in a TDD style we start with the most basic case in this case I would try out the method until oyu can get your method passing:

public int makeCoffee(int recipeToPurchase, int amtPaid) { 
   getRecipes()[recipeToPurchase]getPrice();
   return 25;
}

Another good practice thing is to use as little logic in your test as possible.

rather the:

Recipe r = spy(stubRecipies[0]);

try:

Recipe r = spy(recipe1);

More significantly, the spied object is not being used in CoffeeMaker:

Recipe r = spy(stubRecipies[0]);

r is the mock, and is not the same object as in your array, you can quickly prove this with:

Assert.assertEquals(stubRecipies[0], r);
Assert.assertTrue(stubRecipies[0]== r);

the assertTrue will fail, meaning that stubRecipies[0] is not the same object as the spy (r)

if you set the first index in the array to equal your spied object you might find things work better

MartinByers
  • 1,240
  • 1
  • 9
  • 15
  • as to your first suggestion, i applied what you suggested and got the same test result , the logic is fine and i'm certain the method is called (otherwise irrelevant prices would return & price tests would fail) the problem is that the spy is not capturing those calls. as to the second suggestion . i applied it as well and the assertion failed in either ways (Recipe r = spy(stubRecipies[0]) OR spy(recipe1) ) , and as far as i know that should be the case because a spy object is not supposed to equal the real object, instead it should capture interactions with it, thanks for taking the time – A.Mahmoud Jun 03 '20 at 00:28
  • What spying does is creates a wrapper of your object so it can track interactions. feel free to have a read of this: https://www.baeldung.com/mockito-spy. I have given you and some suggestions to help you solve this yourself (most people learn better that way) and I have also included the answer. – MartinByers Jun 03 '20 at 08:27
  • 2
    I get it now thanks to your helpful suggestion, this is how i refactored my test r = spy(recipe1); stubRecipies[0] = r; verify(r,times(2)).getPrice(); thank you for your time and effort and of course for tolerating me, please tell me if this question needs to be deleted to not confuse other people having a related issue – A.Mahmoud Jun 03 '20 at 22:15
  • No worries, the only thing I would ask is if you found my answer useful please can you vote it up and or accept it. Thanks – MartinByers Jun 05 '20 at 08:05