Testing the service layer

Service layer testing is a critical part of the enterprise application development. As mentioned previously, the service layer encapsulates the business rules that define the working application and is where a significant amount of development time is spent. Business logic evolves as the application is enhanced, new modules are added, and business rules change. The test cases for the service layer will therefore represent the evolution of the application. Well-documented test cases will enhance the knowledge base of the application lifecycle, define changes, and explain the purpose of the change. The service layer test cases will become a repository of information appreciated by all developers working on the project.

The only change required to enable service layer testing is to add the following to the testingContext.xml file defined in the previous chapter:

<context:component-scan base-package="com.gieman.tttracker.service" />

Test case classes added to the directory src/test/java/com/gieman/tttracker/service will then be available for testing. We will add the following classes to the service package:

Testing the service layer

The AbstractServiceForTesting superclass will once again extend AbstractTransactionalJUnit4SpringContextTests, define the @ContextConfiguration configuration file, and override the default Spring logger with the slf4j logger.

package com.gieman.tttracker.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;

@ContextConfiguration("/testingContext.xml")
public abstract class AbstractServiceForTesting extends AbstractTransactionalJUnit4SpringContextTests {

    final protected Logger logger = LoggerFactory.getLogger(this.getClass());
        
}
The CompanyServiceTest class is defined as:
package com.gieman.tttracker.service;

import com.gieman.tttracker.dao.ProjectDao;
import com.gieman.tttracker.domain.Company;
import com.gieman.tttracker.domain.Project;
import com.gieman.tttracker.vo.Result;
import java.util.List;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class CompanyServiceTest extends AbstractServiceForTesting {

    protected final String TEST_USERNAME = "bjones";
    @Autowired
    protected CompanyService companyService;    
    @Autowired
    protected ProjectDao projectDao; 
    
    @Test
    public void testFind() throws Exception {
        
        logger.debug("
STARTED testFind()
");
        Result<List<Company>> allItems = companyService.findAll(TEST_USERNAME);
        
        assertTrue(allItems.getData().size() > 0);

        // get the first item in the list
        Company c1 = allItems.getData().get(0);

        int id = c1.getId();

        Result<Company> c2= companyService.find(id, TEST_USERNAME);

        assertTrue(c1.equals(c2.getData()));
        logger.debug("
FINISHED testFind()
");
    }    
    
    @Test
    public void testFindAll() throws Exception {
        
        logger.debug("
STARTED testFindAll()
");
        int rowCount = countRowsInTable("ttt_company");
        
        if(rowCount > 0){                       
            
            Result<List<Company>> allItems = companyService.findAll(TEST_USERNAME);
            assertTrue("Company.findAll list not equal to row count of table ttt_company", rowCount == allItems.getData().size());
            
        } else {
            throw new IllegalStateException("INVALID TESTING SCENARIO: Company table is empty");
        }
        logger.debug("
FINISHED testFindAll()
");
    }    

    @Test
    public void testAddNew() throws Exception {
        
        logger.debug("
STARTED testAddNew()
");
        //Company c = new Company();
        final String NEW_NAME = "New Test Company name";
        //c.setCompanyName(NEW_NAME);
        
        Result<Company> c2 = companyService.store(null, NEW_NAME, TEST_USERNAME);
        
        assertTrue(c2.getData().getId() != null);
        assertTrue(c2.getData().getCompanyName().equals(NEW_NAME));
        
        logger.debug("
FINISHED testAddNew()
");
    }
    
    @Test
    public void testUpdate() throws Exception {
        
        logger.debug("
STARTED testUpdate()
");
        final String NEW_NAME = "Update Test Company New Name";
        
        Result<List<Company>> ar1 = companyService.findAll(TEST_USERNAME);
        Company c = ar1.getData().get(0);
        
        companyService.store(c.getIdCompany(), NEW_NAME, TEST_USERNAME);
        
        Result<Company> ar2 = companyService.find(c.getIdCompany(), TEST_USERNAME);
        
        assertTrue(ar2.getData().getCompanyName().equals(NEW_NAME));
        
        logger.debug("
FINISHED testMerge()
");
        
    }    
    
    @Test
    public void testRemove() throws Exception {
        
        logger.debug("
STARTED testRemove()
");
        Result<List<Company>> ar1 = companyService.findAll(TEST_USERNAME);
        Company c = ar1.getData().get(0);
        
        Result<Company> ar = companyService.remove(c.getIdCompany(), TEST_USERNAME);        
        Result<Company> ar2 = companyService.find(c.getIdCompany(), TEST_USERNAME);
                
        // should fail as projects are assigned
        assertTrue(! ar.isSuccess());
        // finder still works
        assertTrue(ar2.getData() != null);

        logger.debug("
testRemove() - UNABLE TO DELETE TESTS PASSED
");
        // remove all the projects
        c = ar2.getData();
        
        for(Project p : c.getProjects()){
            projectDao.remove(p);
            
        }
        c.getProjects().clear();

        logger.debug("
testRemove() - removed all projects
");
        
        ar = companyService.remove(c.getIdCompany(), TEST_USERNAME);
        // remove should have succeeded
        assertTrue(ar.isSuccess());
        
        ar2 = companyService.find(c.getIdCompany(), TEST_USERNAME);
        // should not have been found
        assertTrue(ar2.getData() == null);
        assertTrue(ar2.isSuccess());
        
        logger.debug("
FINISHED testRemove()
");
    }     
}

Running this test case by right-clicking on the file in the editor and selecting the Test File option should result in the following output:

Testing the service layer

The UserServiceTest class is defined as:

package com.gieman.tttracker.service;

import com.gieman.tttracker.dao.TaskLogDao;
import com.gieman.tttracker.dao.UserDao;
import com.gieman.tttracker.domain.TaskLog;
import com.gieman.tttracker.domain.User;
import com.gieman.tttracker.vo.Result;
import java.util.Calendar;
import java.util.List;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class UserServiceTest extends AbstractServiceForTesting {

    @Autowired
    protected UserService userService;
    @Autowired
    protected TaskLogDao taskLogDao;
    @Autowired
    protected UserDao userDao;
    private final String TEST_USERNAME = "jsmith";

    @Test
    public void testAddNew() throws Exception {

        String ADMIN_USERNAME = "bjones";

        logger.debug("
STARTED testAddNew()
");

        Result<User> ar = userService.store("nusername", "David", "Francis", "[email protected]", "admpwd", 'N', ADMIN_USERNAME);

        // should succeed
        logger.debug(ar.getMsg());
        assertTrue(ar.isSuccess());

        ar = userService.store(this.TEST_USERNAME, "David", "Francis", "[email protected]", "admpwd", 'Y', ADMIN_USERNAME);
        logger.debug(ar.getMsg());
        assertTrue("Cannot assign email that is currently assigned to other user", !ar.isSuccess());

        ar = userService.store("user100", "David", "Francis", "[email protected]", "", 'Y', ADMIN_USERNAME);

        logger.debug(ar.getMsg());
        assertTrue("Cannot set empty password for user", !ar.isSuccess());

        ar = userService.store("user101", "David", "Francis", "  ", "validpwd", 'Y', ADMIN_USERNAME);

        logger.debug(ar.getMsg());
        assertTrue("Cannot set empty email for user", !ar.isSuccess());

        ar = userService.store(this.TEST_USERNAME, "David", "Francis", "[email protected]", "validpwd", 'Y', ADMIN_USERNAME);

        logger.debug(ar.getMsg());
        assertTrue("Assigning new email to user is allowed", ar.isSuccess());

        logger.debug("
FINISHED testAddNew()
");
    }

    @Test
    public void testRemove() throws Exception {

        String ADMIN_USERNAME = "bjones";
        Calendar DEFAULT_START_DATE = Calendar.getInstance();
        Calendar DEFAULT_END_DATE = Calendar.getInstance();
        DEFAULT_START_DATE.set(Calendar.YEAR, 1900);
        DEFAULT_END_DATE.set(Calendar.YEAR, 3000);
        
        logger.debug("
STARTED testRemove()
");

        User user1 = userDao.find(TEST_USERNAME);

        List<TaskLog> logs = taskLogDao.findByUser(user1, DEFAULT_START_DATE.getTime(), DEFAULT_END_DATE.getTime());
        Result<User> ar;

        if (logs.isEmpty()) {

            ar = userService.remove(TEST_USERNAME, ADMIN_USERNAME);
            logger.debug(ar.getMsg());
            assertTrue("Delete of user should be allowed as no task logs assigned!", ar.isSuccess());

        } else {

            // this user has task log assigned
            ar = userService.remove(TEST_USERNAME, ADMIN_USERNAME);
            logger.debug(ar.getMsg());
            assertTrue("Cascading delete of user to task logs not allowed!", !ar.isSuccess());

        }

        logs = taskLogDao.findByUser(user1, DEFAULT_START_DATE.getTime(), DEFAULT_END_DATE.getTime());
        if (logs.isEmpty()) {

            ar = userService.remove(TEST_USERNAME, ADMIN_USERNAME);
            logger.debug(ar.getMsg());
            assertTrue("Delete of user should be allowed as empty task log list!", ar.isSuccess());

        } else {

            // this user has task log assigned
            ar = userService.remove(TEST_USERNAME, ADMIN_USERNAME);
            logger.debug(ar.getMsg());
            assertTrue("Cascading delete of user to task logs not allowed!", !ar.isSuccess());

        }

        ar = userService.remove(ADMIN_USERNAME, ADMIN_USERNAME);
        logger.debug(ar.getMsg());
        assertTrue("Should not be able to delete yourself", !ar.isSuccess());

        logger.debug("
FINISHED testRemove()
");
    }

    @Test
    public void testLogon() {

        Result<User> ar = userService.findByUsernamePassword("jsmith", "admin");

        assertTrue("Valid user could not be found for valid user/pwd", ar.getData() != null);
        assertTrue(ar.isSuccess());

        ar = userService.findByUsernamePassword("jsmith", "ADMIN");

        assertTrue("Invalid logic - valid user found with UPPERCASE password", ar.getData() == null);
        assertTrue(!ar.isSuccess());

        ar = userService.findByUsernamePassword("[email protected]", "admin");

        assertTrue("Valid user could not be found for valid email/pwd", ar.getData() != null);
        assertTrue(ar.isSuccess());

        ar = userService.findByUsernamePassword("jsmith", "invalidadmin");
        assertTrue("Invalid user verified with wrong password", ar.getData() == null);
        assertTrue(!ar.isSuccess());

        ar = userService.findByUsernamePassword("blah", "blah");
        assertTrue("Invalid user verified with wrong username and password", ar.getData() == null);
        assertTrue(!ar.isSuccess());
    }
}

Note we have not yet defined the implementation of the UserService interface but we have already written test cases. Thanks to the use of Java interfaces, we are able to define test cases before the implementation has been coded. This is one of the key concepts of Test-driven Development (TDD), where developers write test cases that define the desired behavior before writing the actual code that passes the tests. This strategy is also part of the test-first programming concept of Extreme Programming (http://en.wikipedia.org/wiki/Extreme_programming), where test cases are written before the implementation coding starts.

Executing the UserServiceTest test file when the UserServiceImpl has been coded should result in the following output:

Testing the service layer
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.113.163