10008---Trail ~ Testing the DAO
2016-04-02 19:55
525 查看
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.
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 antyunitinittarget.
----------------------
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.
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
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
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.
Right-click cuppytrail/testsrc/de/hybris/platform/cuppytrail/daos/impl/DefaultStadiumDAOIntegrationTest.java in Eclipse's Package Explorer and select RunAs|JunitTest
Execution will fail (you will get unhandled Exceptions) because an implementation of StadiumDAO cannot be found. We will write this next.
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.
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.
If you have problems running the unit tests, please check the following:
Make sure that you have activated the right extensions for the JUnit tenant
Make sure that you have initiated the JUnit tenant
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
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
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 antyunitinittarget.
----------------------
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 illustrateshow 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
Right-click cuppytrail/testsrc/de/hybris/platform/cuppytrail/daos/impl/DefaultStadiumDAOIntegrationTest.java in Eclipse's Package Explorer and select RunAs|JunitTestExecution 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
Make sure that you have activated the right extensions for the JUnit tenantMake sure that you have initiated the JUnit tenant
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
相关文章推荐
- 分类模型的性能评估——以SAS Logistic回归为例(3): Lift和Gain
- hdu 2476 String painter 区间dp 难
- tail -f -F是个好东西
- idea修改文件名后出现main method should be static错误
- Design Pattern Explained 读书笔记二——设计模式序言
- 【POJ1765】November Rain——扫描线+线段树
- LeetCode. Container With Most Water
- IBM openblockchain学习(三)--Ledger源码分析
- 【LeetCode】220. Contains Duplicate III
- error: command 'gcc' failed with exit status 1 的解决办法
- RAID5工作原理介绍
- 219. Contains Duplicate II
- LeetCode 336. Palindrome Pairs
- DrawableContainer
- lintcode-medium-Number of Airplanes in the Sky
- 使用MacBook Air的4项基本技巧
- Xenomai on the Beaglebone Black in 14 easy steps ---fwqlzz love is for ever
- 11. Container With Most Water
- 阶梯博弈(Stair Nim)
- leetcode 172. Factorial Trailing Zeroes