10008---Trail ~ Testing the DAO



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:

  • 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.

@Test
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值