user3123690
user3123690

Reputation: 1241

Junit/Mockito unit with Spring bean dependencies

I need to test following method inside ProductManager class which is a Spring bean. productService a bean and injected into ProductManager class. I try to write a Junit test using Mockito, but it kept to invoke real productService.getProductIdList(email) method instead of mock. ProductService also has a @PostConstruct. Could anyone enlighten me what's wrong with my test code?

@Named(ProductManager.NAME)
public class ProductManager {

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

    public static final String NAME = "productManager";

    public Set<Product> getProducts(String email) {
        Set<Integer> productList = productService.getProductIdList(email);
        Iterator<Integer> itr = productList.iterator();
        Set<Product> products = new HashSet<Product>();
        Product p = null;
        while (itr.hasNext()) {
            p = getProduct(itr.next());
            if (p != null) {
                products.add(p);
            }
        }
        return products;
    }


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

}

So far, I have following Junit test code.

@Test
public void getProductByEmail(){

    String email = "[email protected]";

    ProductService  mockProductService = mock(ProductServiceImpl.class);

    Integer productId1 = 10000;
    Integer productId2 = 10002;

    Product p1 = mock(Product.class);
    Product p2 = mock(Product.class);
    when(p1.getProductId()).thenReturn(productId1);
    when(p2.getProductId()).thenReturn(productId2);

    Set<Integer> productIdSet = (Set<Integer>)mock(Set.class);
    productIdSet.add(productId1);
    productIdSet.add(productId2);
    Iterator<Integer> productIdIterator = (Iterator<Integer>)mock(Iterator.class);
    when(productIdSet.iterator()).thenReturn(productIdIterator);        
    when(mockProductService.getProductIdList(email)).thenReturn(productIdSet);
    when(productIdIterator.hasNext()).thenReturn(true, true, false);
    when(productIdIterator.next()).thenReturn(productId1).thenReturn(productId2);
    when(productManager.getProduct(productId1)).thenReturn(p1);
    when(productManager.getProduct(productId2)).thenReturn(p2);

    Set<Product> products = productManager.getProducts(email);
    assertEquals(2, products.size());

}

Upvotes: 0

Views: 1031

Answers (2)

user3123690
user3123690

Reputation: 1241

I am answering to my own question. I resolved the issue using Spring ReflectionTestUtils. It can set mock dependencies. I referenced http://romiawasthy.blogspot.com/2012/03/autowired-mockobjects-for-junits.html. I tried second solution, but I couldn't make it work. In this Test, the productManager is not mocked. It is a real spring bean. Another way of doing is don't use Spring context at all, just use Mockito RunWith(MockitoJUnitRunner.class). In this case all beans are mocked including the productManager. I did some research yesterday and using MockitoJUnitRunner.class is preferable since it can void reapeating code and you have full control of your test environment. Please look at this article for using MockitoJUnitRunner.class http://www.jayway.com/2012/02/25/mockito-and-dependency-injection/. It is clear and simple.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:context-test.xml")
public class ProductManagerTest {

    @Inject
    private ProductManager productManager;
    @Test
    public void getProductByEmail(){

        String email = "[email protected]";

        ProductService  mockProductService = mock(ProductServiceImpl.class);

        Integer productId1 = 10000;
        Integer productId2 = 10002;

        Product p1 = mock(Product.class);
        Product p2 = mock(Product.class);
        p1.setProductId(productId1);
        p2.setProductId(productId2);


        Set<Integer> productIdSet = (Set<Integer>)mock(Set.class);
        productIdSet.add(productId1);
        productIdSet.add(productId2);
        Iterator<Integer> productIdIterator = (Iterator<Integer>)mock(Iterator.class);
        when(productIdSet.iterator()).thenReturn(productIdIterator);        
        when(mockProductService.getProductIdList(email)).thenReturn(productIdSet);

        ReflectionTestUtils.setField(productManager, "mockProductService",
            mockProductService); 

        when(productIdIterator.hasNext()).thenReturn(true, true, false);
        when(productIdIterator.next()).thenReturn(productId1).thenReturn(productId2);
        when(productManager.getProduct(productId1)).thenReturn(p1);
        when(productManager.getProduct(productId2)).thenReturn(p2);

        Set<Product> products = productManager.getProducts(email);
        assertEquals(2, products.size());

    }
}

Upvotes: 1

DaveH
DaveH

Reputation: 7335

I don't see any where where you set your mocked ProductService in to the ProductManager object.

You've created a intricate set of related objects, but not asked the ProductManager to use it.

Upvotes: 2

Related Questions