10010---Trail ~ Testing the Façades
发布日期:2021-06-28 19:53:02 浏览次数:2 分类:技术文章

本文共 10947 字,大约阅读时间需要 36 分钟。

Motivation

In this step you will learn how to write a façade using TDD (Test Driven Development).

The role of a Façade can be defined as follows:

  • A Façade provides business level methods to the client, hiding any implementation details of services
  • It forwards calls to the appropriate services
  • and packs data returned from the services into a data transfer object.

The following diagram shows how StadiumFacades interacts with other elements used in the trail:

In this trail step, we create an integration test for the Stadium Façade, 

resolve any potential problems that may arise and finally make the test pass.

Background on Façades

The intent of a Façade is to "Provide a unified interface to a set of interfaces in a subsystem. The Façade pattern defines a higher-level interface that makes the subsystem easier to use.": see . In our case, the Façade is the front-most API to which the client (web-pages) has access.

Consider for example a rich client in which the communication between the client and server should be kept to a minimum. If the client needs data from methodA in ServiceA, methodB in serviceB and methodC in serviceC, it would be more efficient for the client to be able to make one call to a Façade on the server that itself calls those 3 methods on the 3 services, rather than to call the 3 services itself. And since the Façade is making the calls, we can also ask the Façade to package the particular data we need (which might be somewhat duplicated in the 3 return values from the 3 services, or might not yet be complete), and to pass that back to the Client. This is the purpose of the Data Transfer Object: see  and . Our façade will be performing both roles itself. You might choose to have a separate DTOAssembler in your own code (in accordance with the design principle Separation of Concerns), and we may modify this trail to also do that.

Create the data transfer objects

We create the Data objects in a declarative way, i.e. define beans and enumerations in an xml file used as input for code generating.

The main advantage is that you can merge attributes over several extensions - in the same way as it is possible with type definitions.

 By doing so you make your façade layer easily extensible.

1.Add to 
cuppytrail/resources/cuppytrail-beans.xml

Data object for MatchSummary which has no equivalent on the type system
Data object representing a Stadium
2. 
Run 
ant clean all
 and refresh the workspace. (ant all is OK)

  Have a look at the created data object classes: StadiumData and MatchSummaryData.

Write the interface

Create the interface:

cuppytrail/src/de/hybris/platform/cuppytrail/facades/StadiumFacade.java
package de.hybris.platform.cuppytrail.facades; import de.hybris.platform.cuppytrail.data.StadiumData; import java.util.List;  public interface StadiumFacade{    StadiumData getStadium(String name);     List
getStadiums(); }

Key points to note:

  • the interface is similar to the StadiumService, with the key difference that 
          the methods are returning StadiumData
POJO
s, rather than
StadiumModel
s.

Write the implementation

cuppytrail/src/de/hybris/platform/cuppytrail/facades/impl/DefaultStadiumFacade.java
And in cuppytrail's Spring configuration:

Write the unit test

The integration test is useful for testing and demonstrating the expected behavior of any class that implements the StadiumFacade interface. But to test this particular implementation of the interface in detail we need a unit test that mocks out the classes on which the implementation depends. Again this is done with Mockito.

Copy the following code to cuppytrail/testsrc/de/hybris/platform/cuppytrail/facades/impl/DefaultStadiumFacadeUnitTest.java

/** * */package de.hybris.platform.cuppytrail.facades.impl; import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertNotNull;import static org.mockito.Mockito.mock;import static org.mockito.Mockito.when; import de.hybris.platform.cuppytrail.StadiumService;import de.hybris.platform.cuppytrail.data.StadiumData;import de.hybris.platform.cuppytrail.model.StadiumModel; import java.util.ArrayList;import java.util.List; import org.junit.Before;import org.junit.Test;  public class DefaultStadiumFacadeUnitTest{    private DefaultStadiumFacade stadiumFacade;     private StadiumService stadiumService;     private final static String STADIUM_NAME = "wembley";    private final static Integer STADIUM_CAPACITY = Integer.valueOf(12345);     // Convenience method for returning a list of Stadium    private List
dummyDataStadiumList() { final StadiumModel wembley = new StadiumModel(); wembley.setCode(STADIUM_NAME); wembley.setCapacity(STADIUM_CAPACITY); final List
stadiums = new ArrayList
(); stadiums.add(wembley); return stadiums; } // Convenience method for returning a Stadium with code and capacity set for wembley private StadiumModel dummyDataStadium() { final StadiumModel wembley = new StadiumModel(); wembley.setCode(STADIUM_NAME); wembley.setCapacity(STADIUM_CAPACITY); return wembley; } @Before public void setUp() { // We will be testing the POJO DefaultStadiumFacade - the implementation of the StadiumFacade interface. stadiumFacade = new DefaultStadiumFacade(); /** * The facade is expected to make calls to an implementation of StadiumService but in this test we want to verify * the correct behaviour of DefaultStadiumFacade itself and not also implicitly test the behaviour of a * StadiumService. In fact as of writing this class, we do only have the interface StadiumService and no * implementation. This requires that we mock out the StadiumService interface. There are several strong arguments * for following this practice: * * If we were to include a real implementation of StadiumService rather than mocking it out.. * * 1) we will not get "false failures" in DefaultStadiumFacade due to errors in the StadiumService implementation. * Such errors should be caught in tests that are focusing on StadiumService instead. * * 2) The condition could arise where an error in the facade gets hidden by a complimentary error in the * StadiumService implementation - resulting in a "false positive". * * By mocking out the interface StadiumService.. * * 3) we do not actually need an implementation of it. This therefore helps us to focus our tests on this POJO * before having to implement other POJOs on which it depends - allowing us to write tests early. * * 4) by focusing on the behaviour of the facade and the interfaces it uses, we are forced to focus also on the * those interface, improving them before writing their implementation. * * * Therefore we create a mock of the StadiumService in the next line. */ stadiumService = mock(StadiumService.class); // We then wire this service into the StadiumFacade implementation. stadiumFacade.setStadiumService(stadiumService); } /** * The aim of this test is to test that: * * 1) The facade's method getStadiums makes a call to the StadiumService's method getStadiums * * 2) The facade then correctly wraps StadiumModels that are returned to it from the StadiumService's getStadiums * into Data Transfer Objects of type StadiumData. */ @Test public void testGetAllStadiums() { /** * We instantiate an object that we would like to be returned to StadiumFacade when the mocked out * StadiumService's method getStadiums is called. This will be a list of two StadiumModels. */ final List
stadiums = dummyDataStadiumList(); // create wembley stadium for the assert comparison final StadiumModel wembley = dummyDataStadium(); // We tell Mockito we expect StadiumService's method getStadiums to be called, and that when it is, stadiums should be returned when(stadiumService.getStadiums()).thenReturn(stadiums); /** * We now make the call to StadiumFacade's getStadiums. If within this method a call is made to StadiumService's * getStadiums, Mockito will return the stadiums instance to it. Mockito will also remember that the call was * made. */ final List
dto = stadiumFacade.getStadiums(); // We now check that dto is a DTO version of stadiums.. assertNotNull(dto); assertEquals(stadiums.size(), dto.size()); assertEquals(wembley.getCode(), dto.get(0).getName()); assertEquals(wembley.getCapacity().toString(), dto.get(0).getCapacity()); } @Test public void testGetStadium() { /** * We instantiate an object that we would like to be returned to StadiumFacade when the mocked out * StadiumService's method getStadium is called. This will be the StadiumModel for wembley stadium. */ // create wembley stadium final StadiumModel wembley = dummyDataStadium(); // We tell Mockito we expect StadiumService's method getStadiumForCode to be called, and that when it is, wembley should be returned when(stadiumService.getStadiumForCode("wembley")).thenReturn(wembley); /** * We now make the call to StadiumFacade's getStadium. If within this method a call is made to StadiumService's * getStadium, Mockito will return the wembley instance to it. Mockito will also remember that the call was made. */ final StadiumData stadium = stadiumFacade.getStadium("wembley"); // We now check that stadium is a correct DTO representation of the ServiceModel wembley assertEquals(wembley.getCode(), stadium.getName()); assertEquals(wembley.getCapacity().toString(), stadium.getCapacity()); } }
Key notes:

  • By mocking out the service on which the façade depends, we are able to test the façade in isolation.
  • With Mockito, we can mock out the service, and verify that the expected calls are made from the façade to the service interface.
  • These tests are not just useful when writing the façade, but when maintaining it on an ongoing basis - they remain in the test suite, and run as part of Continuous Integration.
  • The Cuppy Façade Tests listed below are a good resource for learning TDD best practices.
    • cuppy/web/testsrc/de/hybris/platform/cuppy/web/facades/DefaultMatchFacadeTest.java
    • cuppy/web/testsrc/de/hybris/platform/cuppy/web/facades/DefaultPlayerFacadeTest.java
    • cuppy/web/testsrc/de/hybris/platform/cuppy/web/facades/DefaultStatisticsFacadeTest.java

转载地址:https://blog.csdn.net/xxxcyzyy/article/details/51046040 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:10012---Trail ~ MediaConversion
下一篇:10030---5分钟了解Mockito

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年03月31日 11时05分58秒