Sunday, 20 July 2014

Testing Java programs with EasyMock and JUnit

Background

EasyMock has been the first dynamic Mock Object generator, relieving users of hand-writing Mock Objects, or generating code for them.

EasyMock provides Mock Objects by generating them on the fly using Java's proxy mechanism.

We are going to use this to write test cases in Java. For more details you can visit their official website.


Adding Dependencies

As usual I am going to use Ivy to add dependencies to my project (Installing and using Apache Ivy as dependency manager).  You ivy.xml should look something like the following - 

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.    
-->
<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info
        organisation="openSourceForGeeks"
        module="EasyMockDemo"
        status="integration">
    </info>

    <dependencies>
        <dependency org="junit" name="junit" rev="4.11"/>
        <dependency org="org.easymock" name="easymock" rev="3.1"/>
        
    </dependencies>

</ivy-module>


What we want to achieve via this testing and Why...

Consider you have developed a online shopping portal. You have a class Product that holds information of your individual product like  its name and quantity. When you shop you would purchase multiple products. So lets have a class ShoppingBasket that holds a List of Products. Also lets have a service ShoppingService that would calculate total cost of all the product in the ShoppingBasket. In production code you would probably have this service make a database call the get the price of the product but we cannot afford that in the tests. So we will mock this service and use it instead of an actual DB call.


To the code.....

First create a class Product.java

public class Product {
    
    private String name;
    private int quantity;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getQuantity() {
        return quantity;
    }
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

}


Then create a class name ShoppingBasket.java

public class ShoppingBasket {
    
    private String basketName;
    private ShoppingService shoppingService;
    
    private List<Product> products = new ArrayList<Product>();
    
    public int getTotalCost() {
        int totalCost = 0;
        for(Product product: products) {
            totalCost = totalCost + shoppingService.getProductCost(product.getName());
        }
        return totalCost;
    }
    
    public void addProduct(Product product) {
        this.products.add(product);
    }

    public String getBasketName() {
        return basketName;
    }

    public void setBasketName(String basketName) {
        this.basketName = basketName;
    }

    public ShoppingService getShoppingService() {
        return shoppingService;
    }

    public void setShoppingService(ShoppingService shoppingService) {
        this.shoppingService = shoppingService;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    } 

}


Noe create a Service (which would be an interface / production code would have this implementation with DB logic) name ShoppingService.java.

public interface ShoppingService {
    
    int getProductCost(String productName);
    
}

Note : Methods in an interface are bu default public and abstract. Whereas variable are by default public, static and final.

Finally create a Test class that mocks the service and tests out the getProductCost() method.

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import junit.framework.Assert;

import junit.framework.TestCase;


public class ShoppingBasketTest{
    
    private ShoppingBasket shoppingBasket;
    private ShoppingService shoppingServiceMock;
    
    @Before
    public void init(){
        shoppingBasket = new ShoppingBasket();
        shoppingBasket.setBasketName("myBaset");
        shoppingServiceMock = EasyMock.createMock(ShoppingService.class);
        shoppingBasket.setShoppingService(shoppingServiceMock);
    }
    
    @Test
    public void testTotalCost(){
        EasyMock.expect(shoppingServiceMock.getProductCost("Moto E Android Smartphone")).andReturn(7000);
        EasyMock.replay(shoppingServiceMock);
        
        Product motoE = new Product();
        motoE.setName("Moto E Android Smartphone");
        shoppingBasket.addProduct(motoE);
        Assert.assertEquals(7000, shoppingBasket.getTotalCost());
    }

} 
Thats it run the method with annotation @test  using junit. Your test should be successful.





Note : If you make your test class extend TestCase class @before annotation will not work. If you extend TestCase, JUnit treats your test class as an old (pre JUnit 4 class) and picks org.junit.internal.runners.JUnit38ClassRunner to run it. JUnit38ClassRunner does not know about @BeforeClass annotation. You have to put you logic in overridden method setUp().

No comments:

Post a Comment

t> UA-39527780-1 back to top