I am getting following exceptions when I run Junit test.
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'. For example:
when(mock.getArticles()).thenReturn(articles);Also, this error might show up because:
- you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified.
- inside when() you don't call method on mock but on some other object.
- the parent of the mocked class is not public. It is a limitation of the mock engine.
Following is my code and the exception was thrown at the second when statement. I don't think that my test code didn't violate what the exception claims. I spent for a while, but couldn't figure out. Could someone help? What I need to test is getPermProducts method. In the getPermProducts method, I want to ignore isTempProduct method. So, when p1 came, I want it returns false, and when p2 came, I want it returns true etc..
@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()); } } SOLUTION: I updated my test based on enterbios's answer. I used partial mocking, but as enterbios suggested, we should avoid it, but sometimes we need it. I found that we can have both non-mocked and partial mocked class same time.
@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); } } 1 Answer
Your mockProductManager is actually not a mock but an instance of ProductManager class. You shouldn't mock a tested object. The way to mock some methods from tested object is using a spy but I recommend you to not use them, or even to not think about them unless you're fighting with some ugly legacy code. I think your second 'when' should be replaced with assertion, like:
assertFalse(mockProductManager.isTempProduct(p1)); because what you really want to check in this test is if productManager.isTempProduct(p1) returns false for all instances of PermProduct. To verify results of some method calls/objects states you should use assertions. To make your life with assertions easier you can take a look on some helpful libraries like Hamcrest or FEST (). FEST is simpler for beginners I think.
5