Mockito:在整个控制流中注入模拟

发布于 2024-09-27 10:58:18 字数 1408 浏览 3 评论 0原文

我仍在学习mockito,现在我正在学习如何注入mock。

我有一个正在测试的对象,其特定方法依赖于其他对象。这些对象又依赖于其他对象。我想模拟某些东西,并让这些模拟在执行过程中随处使用——整个方法的控制流。

例如,假设有这样的类:

public class GroceryStore {
    public double inventoryValue = 0.0;
    private shelf = new Shelf(5);
    public void takeInventory() {
        for(Item item : shelf) {
            inventoryValue += item.price();
        }
    }
}

public class Shelf extends ArrayList<Item> {
    private ProductManager manager = new ProductManager();
    public Shelf(int aisleNumber){
        super(manager.getShelfContents(aisleNumber);
    }
}

public class ProductManager {
    private Apple apple;
    public void setApple(Apple newApple) {
        apple = newApple;
    }
    public Collection<Item> getShelfContents(int aisleNumber) {
        return Arrays.asList(apple, apple, apple, apple, apple);
    }
}

我需要编写测试代码,其部分内容如下:

....
@Mock
private Apple apple;
... 
when(apple.price()).thenReturn(10.0);
... 

...
@InjectMocks
private GroceryStore store = new GroceryStore();
...
@Test
public void testTakeInventory() {
   store.takeInventory();
   assertEquals(50.0, store.inventoryValue);
}

每当调用 apple.price() 时,我希望使用我的模拟苹果。这可能吗?

编辑:
重要说明...
包含我想要模拟的对象的类确实有该对象的设置器。但是,在我正在测试的级别上,我确实无法处理该类。因此,按照这个示例,虽然 ProductManager 有一个适用于 Apple 的 setter,但我没有办法从 GroceryStore 对象获取 ProductManager。

I'm still learning mockito and right now I'm learning how to inject mocks.

I have an object under test with a particular method that depends on other objects. Those objects, in turn, depend on other objects. I want to mock certain things and have those mocks be used everywhere during execution--throughout the control flow of the method.

For example assume there are classes like:

public class GroceryStore {
    public double inventoryValue = 0.0;
    private shelf = new Shelf(5);
    public void takeInventory() {
        for(Item item : shelf) {
            inventoryValue += item.price();
        }
    }
}

public class Shelf extends ArrayList<Item> {
    private ProductManager manager = new ProductManager();
    public Shelf(int aisleNumber){
        super(manager.getShelfContents(aisleNumber);
    }
}

public class ProductManager {
    private Apple apple;
    public void setApple(Apple newApple) {
        apple = newApple;
    }
    public Collection<Item> getShelfContents(int aisleNumber) {
        return Arrays.asList(apple, apple, apple, apple, apple);
    }
}

I need to write test code with portions along the lines of:

....
@Mock
private Apple apple;
... 
when(apple.price()).thenReturn(10.0);
... 

...
@InjectMocks
private GroceryStore store = new GroceryStore();
...
@Test
public void testTakeInventory() {
   store.takeInventory();
   assertEquals(50.0, store.inventoryValue);
}

Whenever apple.price() is called, I want my mock apple to be the one used. Is this possible?

EDIT:
Important note...
the class that contains the object I want to mock does have a setter for that object. However, I don't really have a handle to that class at the level I'm testing. So, following the example, although ProductManager has a setter for Apple, I don't have a way of getting the ProductManager from the GroceryStore object.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

被你宠の有点坏 2024-10-04 10:58:18

问题是您通过调用 new 而不是注入来创建您所依赖的对象。将ProductManager注入Shelf(例如在构造函数中),并将Shelf注入GroceryStore。然后在测试中使用模拟。如果你想使用@InjectMocks,你必须通过setter方法注入。

通过构造函数,它可能看起来像这样:

public class GroceryStore {
  public double inventoryValue = 0.0;
  private shelf;

  public GroceryStore(Shelf shelf) {
    this.shelf = shelf;
  }

  public void takeInventory() {
    for(Item item : shelf) {
      inventoryValue += item.price();
    }
  }
}

public class Shelf extends ArrayList<Item> {
  private ProductManager manager;

  public Shelf(int aisleNumber, ProductManager manager) {
    super(manager.getShelfContents(aisleNumber);
    this.manager = manager;
  }
}

public class ProductManager {
  private Apple apple;
  public void setApple(Apple newApple) {
    apple = newApple;
  }
  public Collection<Item> getShelfContents(int aisleNumber) {
    return Arrays.asList(apple, apple, apple, apple, apple);
  }
}

然后你可以测试它模拟所有你依赖的对象:

@Mock
private Apple apple;
... 
when(apple.price()).thenReturn(10.0);

@InjectMocks
private ProductManager manager = new ProductManager();

private Shelf shelf = new Shelf(5, manager);
private GroceryStore store = new GroceryStore(shelf);

//Then you can test your store.

The problem is you create objects you depend on by calling new instead of injecting it. Inject ProductManager into Shelf (e.g. in constructor), and inject Shelf into GroceryStore. Then in test use mocks. If you want to use @InjectMocks, you have to inject by setter methods.

By constructor it could look like this:

public class GroceryStore {
  public double inventoryValue = 0.0;
  private shelf;

  public GroceryStore(Shelf shelf) {
    this.shelf = shelf;
  }

  public void takeInventory() {
    for(Item item : shelf) {
      inventoryValue += item.price();
    }
  }
}

public class Shelf extends ArrayList<Item> {
  private ProductManager manager;

  public Shelf(int aisleNumber, ProductManager manager) {
    super(manager.getShelfContents(aisleNumber);
    this.manager = manager;
  }
}

public class ProductManager {
  private Apple apple;
  public void setApple(Apple newApple) {
    apple = newApple;
  }
  public Collection<Item> getShelfContents(int aisleNumber) {
    return Arrays.asList(apple, apple, apple, apple, apple);
  }
}

Then you can test it mocking all the objects you depend on:

@Mock
private Apple apple;
... 
when(apple.price()).thenReturn(10.0);

@InjectMocks
private ProductManager manager = new ProductManager();

private Shelf shelf = new Shelf(5, manager);
private GroceryStore store = new GroceryStore(shelf);

//Then you can test your store.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文