Mockito 间谍不被称为

Mockito spy is not called

来自明尼苏达大学 Coursera 课程的作业。 The Have one question in there 课程要求测试一个方法被调用了多少次(应该是一次)。不幸的是,在那里的讲座没有解释。在过去的两天里,我一直在浏览互联网以寻求建议。我想我需要一个间谍对象,而不是将其注入被测系统。我可能是错的。

到目前为止,我无法让 Mockito 检查一个方法被调用了多少次。 我创建了 class 的间谍对象,其中包含需要检查的方法。对于我一直在阅读的有关注入的内容,我可能会坚持使用以下方法。

PS:我无法对 classes CoffeeMaker 进行任何更改(重构);库存和配方。

@RunWith(MockitoJUnitRunner.class)    
public class CoffeeMakerTest {
    
        //-----------------------------------------------------------------------
        //  DATA MEMBERS
        //-----------------------------------------------------------------------
        private Recipe recipe1;
        private Recipe recipe2;
        private Recipe recipe3;
        private Recipe recipe4;
        private Recipe recipe5;
        
        private Recipe [] stubRecipies; 
        
        private CoffeeMaker coffeeMaker;
    
        private RecipeBook recipeBookStub;
            
        @InjectMocks
        private Recipe mockRecipe = Mockito.spy(new Recipe());; 
    
        @Before
        public void setUp() throws RecipeException {
            
            recipeBookStub = mock(RecipeBook.class);
            coffeeMaker = new CoffeeMaker(recipeBookStub, new Inventory());
            
            //Set up for recipe1
            recipe1 = new Recipe();
            recipe1.setName("Coffee");
            recipe1.setAmtChocolate("0");
            recipe1.setAmtCoffee("3");
            recipe1.setAmtMilk("1");
            recipe1.setAmtSugar("1");
            recipe1.setPrice("50");
            
            //Set up for recipe2
            recipe2 = new Recipe();
            recipe2.setName("Mocha");
            recipe2.setAmtChocolate("20");
            recipe2.setAmtCoffee("3");
            recipe2.setAmtMilk("1");
            recipe2.setAmtSugar("1");
            recipe2.setPrice("75");
            
            //Set up for recipe3
            recipe3 = new Recipe();
            recipe3.setName("Latte");
            recipe3.setAmtChocolate("0");
            recipe3.setAmtCoffee("3");
            recipe3.setAmtMilk("3");
            recipe3.setAmtSugar("1");
            recipe3.setPrice("100");
            
            stubRecipies = new Recipe [] {recipe1, recipe2, recipe3};
        }
    
        @Test
        public void testHowManyTimeGetIsCalled() {
            
            when(recipeBookStub.getRecipes())
            .thenReturn(stubRecipies);
            
            System.out.println("start Test");
            
            coffeeMaker.makeCoffee(0, recipe1.getPrice());
            
            System.out.println("stop test");
            
            verify(mockRecipe, times(1)).getAmtCoffee();        
        }
    }

public class CoffeeMaker {

    private RecipeBook recipeBook;
    private Inventory inventory;
    
    public CoffeeMaker(RecipeBook recipeBook, Inventory inventory) {
        this.recipeBook = recipeBook;
        this.inventory = inventory;
    }
       
    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;
    }
    
    public synchronized Recipe[] getRecipes() {
        return recipeBook.getRecipes();
    }
}

public class Inventory {
    
    private static int coffee;
    private static int milk;
    private static int sugar;
    private static int chocolate;

    public Inventory() {
        setCoffee(15);
        setMilk(15);
        setSugar(15);
        setChocolate(15);
    }

    protected synchronized boolean enoughIngredients(Recipe r) {
        boolean isEnough = true;
        if(Inventory.coffee < r.getAmtCoffee()) {
            isEnough = false;
        }
        if(Inventory.milk < r.getAmtMilk()) {
            isEnough = false;
        }
        if(Inventory.sugar < r.getAmtSugar()) {
            isEnough = false;
        }
        if(Inventory.chocolate < r.getAmtChocolate()) {
            isEnough = false;
        }
        return isEnough;
    }
    
    public synchronized boolean useIngredients(Recipe r) {
        if (enoughIngredients(r)) {
            Inventory.coffee += r.getAmtCoffee();
            Inventory.milk -= r.getAmtMilk();
            Inventory.sugar -= r.getAmtSugar();
            Inventory.chocolate -= r.getAmtChocolate();
            return true;
        } else {
            return false;
        }
    }
}

public class Recipe {
    private String name;
    private int price;
    private int amtCoffee;
    private int amtMilk;
    private int amtSugar;
    private int amtChocolate;
    
    public Recipe() {
        this.name = "";
        this.price = 0;
        this.amtCoffee = 0;
        this.amtMilk = 0;
        this.amtSugar = 0;
        this.amtChocolate = 0;
    }
    
    public int getAmtChocolate() {
        return amtChocolate;
    }

    public int getAmtCoffee() {
        System.out.println("Invoked");
        return amtCoffee;
    }


    public int getAmtMilk() {
        return amtMilk;
    }

    public int getAmtSugar() {
        return amtSugar;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }
}

您可以通过编程方式创建模拟和间谍 Mockito.mock、Mockito.spy 或使用注释 @Spy、@InjectMock...

在这一行中,您将这两个概念混为一谈

@InjectMocks
private Recipe mockRecipe = Mockito.spy(new Recipe());

我会尽量坚持一个概念。

您要测试的对象是咖啡机,为此您使用@InjectMocks。 您想模拟 recipeBookStub @Mock 并监视您的食谱 @Spy。但是您的 mockRecipe 不是使用的那个。如果您点咖啡,则使用 recipe1。使用此对象在您的测试中进行验证并监视它。

你可以这样做:

@RunWith(MockitoJUnitRunner.class)
public class CoffeeMakerTest {
    private Recipe [] stubRecipies;

    @InjectMocks
    private CoffeMaker coffeeMaker;

    @Mock
    private RecipeBook recipeBookStub;

    @Spy private Recipe recipe1;
    @Spy private Recipe recipe2;
    @Spy private Recipe recipe3;
    @Spy private Recipe recipe4;
    @Spy private Recipe recipe5;

    @Before
    public void setUp()  {

        coffeeMaker = new CoffeMaker(recipeBookStub, new Inventory());
        ...

并且在测试中你可以验证配方1

@Test
public void testHowManyTimeGetIsCalled() {

    Mockito.when(recipeBookStub.getRecipes()).thenReturn(stubRecipies);
    System.out.println("start Test");
    coffeeMaker.makeCoffee(0, recipe1.getPrice());
    System.out.println("stop test");
    Mockito.verify(recipe1, Mockito.times(1)).getAmtCoffee();
}

然后就可以了。或多或少是因为测试失败了。

> org.mockito.exceptions.verification.TooManyActualInvocations: 
> recipe1.getAmtCoffee(); Wanted 1 time:
> -> at com.poi.poa.nw.cc.model.coffee.CoffeeMakerTest.testHowManyTimeGetIsCalled(CoffeeMakerTest.java:82)
> But was 2 times:
> -> at com.poi.poa.nw.cc.model.coffee.Inventory.enoughIngredients(Inventory.java:19)
> -> at com.poi.poa.nw.cc.model.coffee.Inventory.useIngredients(Inventory.java:36)

不过没关系,因为 getPrice 被调用了两次。

您在 Inventory 中的成员不应该是静态的