10008---Trail ~ Testing the DAO

来源:互联网 发布:gta5n卡掉帧如何优化 编辑:程序博客网 时间:2024/05/26 02:21


Motivation

In this step, we will develop an interface and implementation for the Stadium DAO - a class responsible for retrieving Stadium data in the persistence layer. 

In line with Test Driven Development, or TDD, we write a test first and then create all classes required to make the test pass. The following diagram illustrates how the DAO relates to other elements of the trail:


Background

There are some major benefits of using Test Driven Development such as:

  • developers are encouraged to think about all required use-cases and interfaces before starting to write code. This tends to produce fewer redundant methods and a clearer time-line.
  • TDD helps developers to remain focused on the goal/requirements and not get lost in implementation details
  • TDD encourages good test coverage
how to start?
TDD means writing tests first. To do that we need our DAO interface to be place, so the test can actually compile. We start out writing an empty DAO interface and add methods to that interface later on as we write the test. We might realize that our DAO interface methods could be simplified and changed accordingly along with the test. After few iterations of fixing interface and adjusting the test, we end up with a final version of our interface.

Initialize the junit tenant

In this trail you will create an integration test, which unlike a unit test requires access both to the database and the hybris Platform environment.
This can easily be done by using the preconfigured junit tenant in which you can execute your JUnit tests isolated from other tenants. This enables you to test business functionality using real item instances.

Initialize the junit tenant as follows:

  • Log in into http://localhost:9001 as admin
  • Click on the tenant-link (Platform/Tenants) 
  • Click on "View" under TenantID junit
  • Make sure, that you check cuppy and cuppytrail and then click "Initialize Tenant" 
Another way of initializing the junit tenant is to run the antyunitinit target.

----------------------

Write the DAO interface

Create the interface cuppytrail/src/de/hybris/platform/cuppytrail/daos/StadiumDAO.java. This describes the CRUD functionality we require, in this case only Read functionality.

package de.hybris.platform.cuppytrail.daos; import de.hybris.platform.core.model.product.ProductModel;import de.hybris.platform.cuppytrail.model.StadiumModel; import java.util.List;  /** * This interface belongs to the Source Code Trail documented at https://wiki.hybris.com/display/pm/Source+Code+Tutorial * An interface for the Stadium DAO. This incorporates the CRUD functionality we require for our DAO tests to pass. */public interface StadiumDAO{    /**     * Return a list of stadium models that are currently persisted. If none are found an empty list is returned.     *     * @return all Stadiums of system     */    List<StadiumModel> findStadiums();     /**     * Finds all stadiums with given code. If none is found, an empty list will be returned.     *     * @param code     *           the code to search for stadiums     * @return All stadiums with the given code.     */    List<StadiumModel> findStadiumsByCode(String code);}

Key points to note:

  • The interface consists of methods required by the test DefaultStadiumDAOIntegrationTest.java. 
  • The comments describe the behavior for both "success and failure" of each method
  • We don't need a "save" method - the saving mechanism is done by "model service"
  • findStadiumsByCode returns a list, but because we have set unique=true in items.xml, the list will have either 1 or 0 elements

Create the DAO integration test

In this step we will write a test to demonstrate the expected behavior of our DAO. The test illustrates

  • how to call the findStadiums method and what it should return when it does and does not find data
  • how to call the findStadiums(String code) method and what it should return when it does and does not find data
  • how to persist a Stadium

This is a bare-bones unit test and we would be well advised to expand it to demonstrate and test that the method succeeds correctly when the parameters are within range, AND that the method fails correctly (and gracefully) when the parameters are out of range.  For example:

  • what happens when findStadiums is called, but with an argument that
    • has incorrect upper/lower case combination 
    • is null
    • is the empty string
    • has a space/invalid characters

By covering such cases, the test file

  • becomes itself an essential resource for developers to learn the behavior of its methods,
  • will notify the developer should he/she break the existing behavior and thus reduce bugs

Create a test class cuppytrail/testsrc/de/hybris/platform/cuppytrail/daos/impl/DefaultStadiumDAOIntegrationTest.java with the following code

/** * [y] hybris Platform * * Copyright (c) 2000-2011 hybris AG * All rights reserved. * * This software is the confidential and proprietary information of hybris * ("Confidential Information"). You shall not disclose such Confidential * Information and shall use it only in accordance with the terms of the * license agreement you entered into with hybris. * * */ package de.hybris.platform.cuppytrail.daos.impl; import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertTrue; import de.hybris.platform.cuppytrail.daos.StadiumDAO;import de.hybris.platform.cuppytrail.model.StadiumModel;import de.hybris.platform.servicelayer.ServicelayerTransactionalTest;import de.hybris.platform.servicelayer.model.ModelService; import java.util.List; import javax.annotation.Resource; import org.junit.Test; /** * This class belongs to the Source Code Trail documented at https://wiki.hybris.com/display/pm/Source+Code+Tutorial * * The purpose of this test is to illustrate DAO best practices and behaviour. * * The DAO logic is factored into a separate POJO. Stepping into these will illustrate how to write and execute * FlexibleSearchQueries - the basis on which DAOs operate. * * @see "https://wiki.hybris.com/display/pm/Trail+~+DAOs" */ public class DefaultStadiumDAOIntegrationTest extends ServicelayerTransactionalTest{    /** As this is an integration test, the class (object) being tested gets injected here. */    @Resource    private StadiumDAO stadiumDAO;     /** Platform's ModelService used for creation of test data. */    @Resource    private ModelService modelService;     /** Name of test stadium. */    private static final String STADIUM_NAME = "wembley";     /** Capacity of test stadium. */    private static final Integer STADIUM_CAPACITY = Integer.valueOf(12345);     @Test    public void stadiumDAOTest()    {        List<StadiumModel> stadiumsByCode = stadiumDAO.findStadiumsByCode(STADIUM_NAME);        assertTrue("No Stadium should be returned", stadiumsByCode.isEmpty());         List<StadiumModel> allStadiums = stadiumDAO.findStadiums();        final int size = allStadiums.size();         final StadiumModel stadiumModel = new StadiumModel();        stadiumModel.setCode(STADIUM_NAME);        stadiumModel.setCapacity(STADIUM_CAPACITY);        modelService.save(stadiumModel);         allStadiums = stadiumDAO.findStadiums();        assertEquals(size + 1, allStadiums.size());        assertEquals("Unexpected stadium found", stadiumModel, allStadiums.get(allStadiums.size() - 1));         stadiumsByCode = stadiumDAO.findStadiumsByCode(STADIUM_NAME);        assertEquals("Did not find the Stadium we just saved", 1, stadiumsByCode.size());        assertEquals("Retrieved Stadium's name attribute incorrect",                        STADIUM_NAME, stadiumsByCode.get(0).getCode());        assertEquals("Retrieved Stadium's capacity attribute incorrect",                        STADIUM_CAPACITY, stadiumsByCode.get(0).getCapacity());    } }


Key points to note:

  • We have written this test before implementing the interface. This encourages us to think about the required behavior before writing any methods helping to write only methods that are required and making sure that all essential tests are covered.
  • Notice that this test class extends ServicelayerTransactionalTest (which, in turn, extends ServicelayerTest).  From ServicelayerTest, our test class inherits some platform-related and persistence-related helper methods.  But by extending ServiceLayerTransactionalTest (i.e., the preferred mechanism), each test method automatically begins a service-layer transaction upon invocation, then automatically performs a rollback upon completion.  This provides a convenient form of isolation all of your service-layer test methods.  For more details on unit-testing in the hybris suite, look here.

Run the test

  1. Right-click cuppytrail/testsrc/de/hybris/platform/cuppytrail/daos/impl/DefaultStadiumDAOIntegrationTest.java in Eclipse's Package Explorer and select RunAs|JunitTest
  2. Execution will fail (you will get unhandled Exceptions) because an implementation of StadiumDAO cannot be found.  We will write this next.

Write the DAO implementation

Write the DAO functionality in the class cuppytrail/src/de/hybris/platform/cuppytrail/daos/impl/DefaultStadiumDAO.java, using hybris' Flexible Search Query for the queries, and the ModelService's save method for saving.

package de.hybris.platform.cuppytrail.daos.impl; import de.hybris.platform.cuppytrail.daos.StadiumDAO;import de.hybris.platform.cuppytrail.model.StadiumModel;import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;import de.hybris.platform.servicelayer.search.FlexibleSearchService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component; @Component(value = "stadiumDAO")public class DefaultStadiumDAO implements StadiumDAO{    /**     * We use hybris' FlexibleSearchService for running queries against the database     *     * @see "https://wiki.hybris.com/display/release5/FlexibleSearch"     */    @Autowired    private FlexibleSearchService flexibleSearchService;     /**     * Finds all Stadiums by performing a FlexibleSearch using the {@link FlexibleSearchService}.     */    @Override    public List<StadiumModel> findStadiums()    {        // Build a query for the flexible search.        final String queryString = //        "SELECT {p:" + StadiumModel.PK + "} "//                + "FROM {" + StadiumModel._TYPECODE + " AS p} ";         final FlexibleSearchQuery query = new FlexibleSearchQuery(queryString);         // Note that we could specify paginating logic by providing a start and count variable (commented out below)        // This can provide a safeguard against returning very large amounts of data, or hogging the database when there are        // for example millions of items being returned.        // As we know that there are only a few persisted stadiums in this use case we do not need to provide this.         //query.setStart(start);        //query.setCount(count);         // Return the list of StadiumModels.        return flexibleSearchService.<StadiumModel> search(query).getResult();    }     /**     * Finds all Stadiums by given code by performing a FlexibleSearch using the {@link FlexibleSearchService}.     */    @Override    public List<StadiumModel> findStadiumsByCode(final String code)    {        final String queryString = //        "SELECT {p:" + StadiumModel.PK + "}" //                + "FROM {" + StadiumModel._TYPECODE + " AS p} "//                + "WHERE " + "{p:" + StadiumModel.CODE + "}=?code ";         final FlexibleSearchQuery query = new FlexibleSearchQuery(queryString);        query.addQueryParameter("code", code);         return flexibleSearchService.<StadiumModel> search(query).getResult();    }}

Further Steps

Extend the integration test to test and demonstrate more use-cases.

The more use cases covered in the test, the more robust it will be and the more "living" documentation a new developer will be able to to turn to.

Add the following code to DefaultStadiumDAOIntegrationTest.java.  Many more failure modes can be added.

@Testpublic void testFindStadiums_EmptyStringParam(){    //calling findStadiumsByCode() with an empty String - returns no results    final List<StadiumModel> stadiums = stadiumDAO.findStadiumsByCode("");    assertTrue("No Stadium should be returned", stadiums.isEmpty());}  @Test(expected = IllegalArgumentException.class)public void testfindStadiums_NullParam(){    //calling findStadiumByCode with null should throw an IllegalArgumentException    stadiumDAO.findStadiumsByCode(null); //method's return value not captured}

If you have problems running the unit tests, please check the following:

 Troubleshooting

  1. Make sure that you have activated the right extensions for the JUnit tenant
  2. Make sure that you have initiated the JUnit tenant
  3. If you have errors connecting to the Database, you might be running HSQLDB with the platform still running. Since you are only allowed a single DB connection with HSQLDB, you will need to shut down the server before running your unit test

0 0
原创粉丝点击