1. ホーム
  2. java

[解決済み] org.mockito.exceptions.misusing.MissingMethodInvocationException

2022-01-31 16:21:30

質問

Junitのテストを実行すると、以下のような例外が発生します。

org.mockito.exceptions.misusing.MissingMethodInvocationException:

when() には引数が必要で、これは 'モックのメソッド呼び出し' である必要があります。 例えば

   when(mock.getArticles()).thenReturn(articles);

また、このエラーは以下の理由で表示されることがあります。

  1. final/private/equals()/hashCode() メソッドのいずれかをスタブにしています。 これらのメソッドは はできません。 スタブ化/検証される。
  2. when() の内部では、モックではなく他のオブジェクトのメソッドをコールしています。
  3. モックされたクラスの親がパブリックでない。 これはモックエンジンの制限事項です。

以下は私のコードで、2つ目のwhen文で例外が発生しました。私のテストコードは例外が主張することに違反していなかったと思います。私はしばらく費やしましたが、理解することができませんでした。誰か助けてくれませんか?私がテストする必要があるのは、getPermProductsメソッドです。getPermProductsメソッドでは、isTempProductメソッドを無視したいのです。つまり、p1が来たらfalseを返し、p2が来たらtrueを返すようにしたいのです。

@Named(ProductManager.NAME)
public class ProductManager {

    @Resource(name = ProductService.NAME)
    private ProductService productService;

    public List<Product> getPermProducts(Set<Product> products) {
        Iterator<Product> it = products.iterator();
        List<Product> cProducts = new ArrayList<Product>();
        Product p;
        while (it.hasNext()) {
            p = it.next();
            if (!isTempProduct(p)) {
            cProducts.add(p);
            }
        }
        return cProducts;
    }

    public Boolean isTempProduct(Product product) {
    if (product instanceof PermProduct) {
    return false;
    }
    Set<ProductItems> pItems = product.getProductItems();
        if (pItems.isEmpty()) {
        return false;
    }
    Iterator<ProductItem> itr = pItems.iterator();
    while (itr.hasNext()) {
        if (itr.next() instanceof TempItem) {
            return true;
        }
    }
    return false;
}

public Product getProduct(Integer productId) {
    Product p = productService.getProduct(productId);
    return p;
    }

}



@RunWith(MockitoJUnitRunner.class)
public class ProductManagerTest {


    @InjectMocks
    private ProductManager mockProductManager;
    @Mock
    private ProductService mockProductService;//not being used here

    private static final Integer PRODUCT_ID_1 = 1;
    private static final Integer PRODUCT_ID_2 = 2;

    @Test
    public void getProduct(){
        Product p1 = mock(PermProduct.class);
            p1.setProductId(PRODUCT_ID_1);
        when(mockProductManager.getProductId()).thenReturn(PRODUCT_ID_1);
        when(mockProductManager.isTempProduct(p1)).thenReturn(false);
            Product p2 = mock(TempProduct.class);
            p2.setProductId(PRODUCT_ID_2);
            when(mockProductManager.isTempProduct(p2)).thenReturn(true);
            List<Product> products = Mock(List.class);
            products.add(p1);
            products.add(p2);
            Iterator<Product> pIterator = mock(Iterator.class);
            when(prodcuts.iterator()).thenReturn(pIterator);
            when(pIterator.hasNext()).thenReturn(true, true, false);
    when(pIterator.next()).thenReturn(p1, p2);
            asserEquals(1, mockProductManager.getPermProducts(products).size());

    }

}

解決策:enterbiosさんの回答に基づいて、私のテストを更新しました。私は部分モックを使いましたが、enterbiosが示唆したように、それは避けるべきですが、時にはそれが必要です。モックされないクラスと部分モックされたクラスを同時に持つことができることがわかりました。

@RunWith(MockitoJUnitRunner.class)
public class ProductManagerTest {

    @InjectMocks
    private ProductManager productManager;//not being used here
    @Mock
    private ProductService mockProductService;//not being used here

    @Spy
    private OtherService other = new OtherService();//not being used here

    @InjectMocks
    final ProductManager partiallyMockedProductManager = spy(new ProductManager());

    private static final Integer PRODUCT_ID_1 = 1;
    private static final Integer PRODUCT_ID_2 = 2;

    @Test
    public void getProduct() {
        Product p1 = new PermProduct();
        p1.setProductId(PRODUCT_ID_1);
        Product p2 = new Product();
        p2.setProductId(PRODUCT_ID_2);
        List<Product> products = new ArrayList<Product>();
        products.add(p1);
        products.add(p2);

        doReturn(false).when(partiallyMockedProductManager).isTempProduct(p1);
        doReturn(true).when(partiallyMockedProductManager).isTempProduct(p2);
        assertEquals(1, partiallyMockedProductManager.getPermProducts(products).size());
        verify(partiallyMockedProductManager).isTempProduct(p1);
        verify(partiallyMockedProductManager).isTempProduct(p2);
    }
}

解決方法は?

mockProductManagerは、実はモックではなく、ProductManagerクラスのインスタンスです。テスト済みのオブジェクトをモック化してはいけません。スパイを使えばテスト済みのオブジェクトからいくつかのメソッドをモックすることができますが、醜いレガシーコードと戦っているのでなければ、スパイを使わない、あるいは、スパイについて考えないようにすることをお勧めします。 2番目の 'when' はアサーションに置き換えるべきだと思います。

assertFalse(mockProductManager.isTempProduct(p1));

なぜなら、このテストで本当に確認したいことは、PermProductのすべてのインスタンスに対してproductManager.isTempProduct(p1)がfalseを返すかどうかだからです。いくつかのメソッドコールやオブジェクトの状態の結果を確認するには、アサーションを使用する必要があります。アサーションを使いやすくするために、HamcrestやFESTのような便利なライブラリを見てみましょう ( http://docs.codehaus.org/display/FEST/Fluent+Assertions+Module ). 初心者はFESTの方が簡単だと思います。