Friday, February 10, 2012

Project 2 - Bean Testing with JUnit

There is a difference between Unit testing, Integration testing, Functional testing, Acceptance testing, etc.  Here we will demonstrate unit testing, and 2 forms of integration testing.  In our AjaxBean, we have a method validation that returns if the name is unique and valid.  Let us test this method in 3 different ways and we can see how we would write the rest of our tests.

AjaxBeanWithoutSpringTest.java

package com.sample.beans;
import com.sample.dao.BasicDao;
import com.sample.dao.OrderDaoImp;
import com.sample.dao.PartDao;
import com.sample.entities.OrderEntity;
import com.sample.entities.PartEntity;
import com.sample.services.OrderService;
import com.sample.services.OrderServiceImp;
import java.util.List;
import javax.faces.validator.ValidatorException;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.springframework.orm.jpa.JpaTemplate;
/**
 * @author Thomas Dias
 */
public class AjaxBeanWithoutSpringTest {
    class TestOrderService implements OrderService {
        public List<PartEntity> getAvailableParts(Long id) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public PartDao getPartDao() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void setPartDao(PartDao partDao) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public BasicDao getDao() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void setDao(BasicDao dao) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public int count() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void create(OrderEntity entity) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void delete(OrderEntity entity) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void edit(OrderEntity entity) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public OrderEntity find(Long id) {
            return null;
        }
        public List<OrderEntity> findAll() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public JpaTemplate getJpaTemplate() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public void remove(Long id) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        public OrderEntity findByName(String name) {
            OrderEntity result = null;
            if (name.equals("Sample Order1")) {
                result = new OrderEntity();
                result.setName(name);
            }
            return result;
        }
        public OrderEntity getOrderEntity(OrderEntity orderEntity) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    };
    public AjaxBeanWithoutSpringTest() {
    }
    @BeforeClass
    public static void setUpClass() throws Exception {
    }
    @AfterClass
    public static void tearDownClass() throws Exception {
    }
    @Before
    public void setUp() {
    }
    @After
    public void tearDown() {
    }
    /**
     * Test of validate method, of class AjaxBean.
     */
    @Test
    public void unitTestValidate() {
        AjaxBean instance = new AjaxBean();
        instance.setOrderService(new TestOrderService());
        String value = "Sample Order1";
        try {
            instance.validate(null, null, null);
            fail("Validation failed to catch error");
        } catch (ValidatorException e) {
        }
        try {
            instance.validate(null, null, "");
            fail("Validation failed to catch error");
        } catch (ValidatorException e) {
        }
        try {
            instance.validate(null, null, value);
            fail("Validation failed to catch error");
        } catch (ValidatorException e) {
        }
        instance.validate(null, null, "no error");
    }
    @Test
    public void integrationTestValidate() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("localTestingPU");
        AjaxBean instance = new AjaxBean();
        OrderServiceImp orderService = new OrderServiceImp();
        OrderDaoImp orderDao = new OrderDaoImp();
        orderDao.setEntityManagerFactory(emf);
        orderService.setDao(orderDao);
        instance.setOrderService(orderService);
        String value = "Sample Order1";
        try {
            instance.validate(null, null, value);
            fail("Validation failed to catch error");
        } catch (ValidatorException e) {
        }
        instance.validate(null, null, "no error");
        emf.close();
    }
}

The first test we have is: unitTestValidate().  What is a unit test?  It is the testing of the smallest piece of an application that can be tested.  So, in our unit test, we call our validate method, but the validate method relies on an OrderService to retrieve the record from the database.  So, using our setter method, we inject a OrderServiceTest class that implements OrderService with methods that return predictable results.  Then we test what happens when we pass a null value, when we pass an empty value, when we pass a value that is supposed to exist, and when we pass a value that is valid.  Because the validation throws an exception, we have to put our checks in try catch blocks.

The second test is an integration test.  It tests the integration of the bean with the database.  We create an EntityManagerFactory and inject it into an OrderServiceImp class that we inject into our bean.  Then we test the a value that should be in the database, and a value that shouldn't.  (I already had it in the database, but it is just as easy to call a method to put the record in the database)  Notice the last line in the test; we have to close the EntityManagerFactory and clean up.

The third test demonstrates an integration test with Spring.
AjaxBeanWithSpringTest.java

package com.sample.beans;
import com.sample.services.OrderService;
import javax.faces.validator.ValidatorException;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.springframework.beans.factory.annotation.Autowired;
/**
 * @author Thomas Dias
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/AjaxBeanWithSpringTest.xml")
public class AjaxBeanWithSpringTest {
    @Autowired
    OrderService orderService;
    public OrderService getOrderService() {
        return orderService;
    }
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
    public AjaxBeanWithSpringTest() {
    }
    @BeforeClass
    public static void setUpClass() throws Exception {
    }
    @AfterClass
    public static void tearDownClass() throws Exception {
    }
    @Before
    public void setUp() {
    }
    @After
    public void tearDown() {
    }
    @Test
    public void integrationTestValidate() {
        AjaxBean instance = new AjaxBean();
        instance.setOrderService(orderService);
        String value = "Sample Order1";
        try {
            instance.validate(null, null, value);
            fail("Validation failed to catch error");
        } catch (ValidatorException e) {
        }
        instance.validate(null, null, "no error");
    }
}
We start this with JUnit, but we annotate it with:
   @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="/AjaxBeanWithSpringTest.xml")
The first annotation runs the test with Spring engaged, the second defines the context.xml that we will run with.  Note:  the path of the xml will be the context path of the test classes.  For me, it was:
/build/test/classes/AjaxBeanWithSpringTest.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="localTestingPU"/>
    </bean>
    <bean id="orderDao" class="com.sample.dao.OrderDaoImp">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <bean id="partDao" class="com.sample.dao.PartDaoImp">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <bean id="orderService" class="com.sample.services.OrderServiceImp">
        <property name="dao" ref="orderDao" />
        <property name="partDao" ref="partDao" />
    </bean>
</beans>

Notice, this creates all the dependencies and injects them into the orderService field via the @Autowire in AjaxBeanWithSpringTest.  We then perform the same tests we did in the last integration test.

From here we can see how we would test the rest.  Injecting mock classes into our beans for unit testing, creating instances of our implementation classes via Spring or JUnit without Spring to inject the beans into our integration tests, and then we could use an embedded container to create the beans and test it in the container.

Note: I only tested one method to demonstrate testing for corner cases, and testing against the database.  Of course, we would need to write tests for all the other methods, etc.

No comments:

Post a Comment