Blog

Test Data Loader Design Pattern by Hycel Taylor on May 06, 2014

Prologue

If you have access to or the ability to automate and generate large amounts of simple to complex test data, it will inevitably force you to rethink how you go about testing your software. We’ve spent a lot of time learning, crafting, and perfecting the best practices for testing with good test data.  In that time, we’ve come up with great design patterns that have enabled us to test more thoroughly, promote projects to production much quicker and with way fewer bugs.  One such design pattern is the Test Data Loader (TDL) Design Pattern.

Introduction

The Test Data Loader (TDL) design pattern provides a model for loading test data using a common set of components that interact with each other via a common interface.

Intent

The Test Data Loader design pattern aims to decouple the loading of test data from other testing concerns, encapsulate the business logic of loading test data and provide a common interface for the transport and consumption of test data.

Solution

Break up the loading of test data into specific concerns and encapsulate the business logic of each concern and provide a common interface from which to transport test data for consumption.

Components Of The TDL Design Pattern

LoaderDesignPattern

  • TestData – may be any size and format of test data to be consumed.
  • TestDataLoader – encapsulates business logic to load and transfer test data.
  • TestDataTransferObject – provides a common interface for test data transport.
  • TestDataConsumer – encapsulates the business logic that consumes the test data for testing purposes.

 

Application-specific example using the TDL design pattern

The application-specific example will show how the TDL design pattern can be used to load user test data to serve two different purposes; populating user test data by means of a user service method and to perform integration testing on the same user service method.

User Test Data Specific Components Using The TDL Design Pattern

The component diagram below shows a concrete example of the TDL design components being used to load user

specific test data. LoaderDesignPattern1

  • User.xml – is a TestData component that happens to be XML for this particular example.
  • UserTestDataLoader – is a TestDataLoader component that encapsulates business logic to load the XML test data.
  • LoaderInfo – is a TestDataTransferObject and provides a common interface to transport the test data.
  • UserTestDataService – is a TestDataConsumer component whose purpose is to load user test data via the UserService.create method.
  • UserServiceTest – is also a TestDataConsumer component whose purpose is to provide the necessary integration tests to fully test all methods defined in the UserService class. For brevity only one integration test will be defined to test the UserService.create method.

 

User Service Create Method Business Logic

The activity diagram below shows the flow of business logic and affected entities when a user is created.

CreateUser

Data Model

The data model below displays the entities that must participate in the creation of a user; some of the entities must be populated prior to the creation of a user while others entities will be affected as a result of the user creation.

TDMExample3

  • organization – defines unique organizations and must be loaded prior to test execution to achieve a successful result.
  • role – defines the different roles of a user and must be loaded prior to test execution to achieve a successful result.
  • department – a department must belong to one and only one organization and must be loaded prior to test execution to achieve a successful result.
  • user – defines user demographic specific information.
  • address – defines address specific information.
  • department_user – allows a user to be associated to one or more departments within a given organization.
  • user_role – allows a user to be associated to one or more roles.
  • user_address – allows a user to be associated to one or more addresses

 

UserService.create()

The example Groovy code below shows the basic business logic of the UserService.create method to create a user and affect other related entities.

Data Transfer Objects

A common data transfer object (DTO) is a key component of the TDL design pattern; it is used to encapsulate other data transfer objects that contain test data.

LoaderInfo

In this instance the LoaderInfo class presents a common interface for test data loaders.

  • parentKey – is used for containing a reference ID to a parent object.  This value is often used in conjunction with the Test Data Map (TDM) design pattern.
  • object – is used to reference the object that contains the imported test data.

 

The attributes defined in this class are not limited to parentKey and object. Other attributes that are useful to two or more DTOs may also be defined here. This class may also be completely ignored and the class that directly defines the test data to be transferred may be used instead, however, a standard interface and code harmony amongst loaders may suffer.

UserDTO

UserDTO contains the specific attributes that describe a user, the user’s address, role and department information.

UserTestDataLoader

The example Groovy code below shows code that encapsulates the business logic for loading test data from an XML file, test/data/User.xml (an actual XML file is not shown in this example).

  • Line 7 is where the data is imported via the Groovy XMLSlurper.
  • Line 13 populates the userDTO instance.
  • Line 15 references the userDTO instance with info.object which represents the common DTO interface.
  • Line 16 references the departmentId with info.parentKey.

 

UserTestDataService

The example Groovy code below shows code that uses the UserTestDataLoader.load() method and the userService.create method to load user test data and populate the users; code that directly uses these two components are

  • line 12 uses the UserTestDataLoader to retrieve user test data.
  • line 37 calls the userService.create service method to store the user test data.

 

User Service Integration Tests

The example Groovy code below shows code that is used to execute integration tests on UserService methods.  In this example, only the create UserService method is tested.

  • line 27 uses the UserTestDataLoader to retrieve user test data.
  • line 52 calls the userService.create service method to store the user test data.

 

Notice how other test data loader services are called in the integration’s setup method (lines 7 – 13) in order to populate other required test data (in this case, organization, roles and departments) prior to executing each test.  Because the other services also use the TDL design pattern, code for loading other required test data is completely decoupled and may in turn be fully tested via other integration tests.

Discussion

In the application-specific example we showed how the TDL design pattern could be used via a concrete set of components to either load user test data into a set of entities using a UserService.create method or to do integration testing on the same UserService.create method.

  • We showed how the TestDataLoader source code encapsulated the business logic to consume the user specific test data.
  • We showed how the LoaderInfo was used as a data transfer object to provide a common interface for test data transport.
  • We showed how the UserTestDataService used the UserTestDataLoader to consume and populate users via the UserService.create method.
  • We showed how the UserServiceTest used the UserTestDataLoader and other test data services to load test data and to perform an integration test on the UserService.create method.

 

Hopefully this application-specific example shows how in using the DTL design pattern with other related test data design patterns it may be possible to fully test any number of services and services methods.

Summary

The TDL design pattern can be used very effectively when testing simple to highly complex and interrelated code and data models.  By decoupling the concerns, behavior and business logic using the TDL design pattern it makes it possible to better define, modify and maintain test data solutions that can quickly evolve and adapt to changes in source code, business rules, data models as well as test data.

Related Design Patterns

The Test Data Map (TDM) design pattern provides a way to map synthetically generated test data relationships to true primary identifiers generated by a target platform.