Using the DataAccessMockFactory

Introduction

Provisionally, the simplest implementation for Unit Testing is to read and write directly to the database as part of the tests. In theory, it’s a good and thorough test, however, you shall eventually run into some subtle problems. Writing tests against the database as a shared resource simply violates the fundamental concept of successful unit testing, in that all unit tests should be independent from each other. A Unit Test relying on a specific database record to exist or have certain values may suddenly fail because an unrelated test has either deleted or modified the record.
Basically, there are potentially too many moving parts. Sure, we can use transactional blocks and perform roll backs in our unit tests, but we are still relying on database integrity.
Mocked Data Test-Driven Development eliminates dependencies on the database (including its contents). The database is agnostic, eradicating the need to use transactional blocks and rollbacks. Therefore, providing our Test-Driven development with Data Integrity.

Mock Data Configuration

Mock Data can be configured and generated through the SmartComponents Business Entity Designer. So, before we can generate Mock Data, we first need to configure the SmartComponents Business Entity Designer

A Business Entity Designer ‘plugin’ is used to enable Mock Data Generation. The plugin is named:

To install the plugin, execute the Business Entity Designer and follow the steps below to install the plugin.

  • From the ‘File’ tab on the ribbon

 

Select ‘Options’ to open the BusinessEntity Designer Settings Dialog.

  • Using ‘Select Plugin’ button located in the top right corner. From the resulting Dialog listing all available plugins, search and select the plugin named

‘DataAccessMockGeneratorPlugin’

  • Once selected, press OK and the plugin shall then be installed and ready for use.

  • Restart the Business Entity Designer

Generating Mock Data

Generating Mock Data is achieved through the ‘Business Entity Tester’. 

In order to generate Mock Data, you should first open the relevant Business Entity that you require to generate Mock Data against. Once the relevant Business Entity is open in the Business Entity Designer; you shall see on the ribbon an option named “Business Entity Tester”.

Select the “Business Entity Tester” option which shall open the Business Entity Tester Window. If you now select the ‘File’ option from the top toolbar menu, you should now see the following:

It is imperative that you perform any Filtering that is required and finally select the Get Data option to populate the Browser within the Business Entity Tester Window, before selecting the ‘Create Data Access Mock’. If the “Get Data” option is not selected, then the Generated Mock Data files shall be empty.

If no filtering is performed on the “Get Data” then the Generated Mock Data Files shall contain a snapshot of all the data at that moment in time. If you specify some filtering, then the Mock Data shall represent that specific subset of data.

Once you have the desired data available in the Business Entity Tester, you can then proceed in generating the Mock Data repository files. From the ‘File’ toolbar option, select the ‘Create Data Access Mock’:

For the Mock Definition, select the textbox ‘Lookup’ option to specify the location of where you wish to save the file. Before selecting OK, ‘Tab’ into the ‘Data File’ field so that this field is then also populated with the location path. When you are satisfied with your location selections, simply select ‘OK’.

The Mock Access Data Repository files shall now be available in the paths you specified.

Mock Definition File

The CustomerDataAccess.mock file is a ‘JSON’ file:

[ { "DataAccess": "Consultingwerk.SmartComponentsDemo.OERA.Sports2000.CustomerDataAccess", "Data": "Consultingwerk/OeraTests/DataAccessMocking/dsCustomer.xml", "Excludes": { "eCustomer": "Flags" } }, { "DataAccess": "Consultingwerk.SmartComponentsDemo.OERA.Sports2000.SalesrepDataAccess", "Data": "Consultingwerk/OeraTests/DataAccessMocking/dsSalesrep.xml" } ]

The ‘.mock’ is always constructed as a ‘JSON’ and can be either a single JSON object or as a JSON Array. To create JSON Array, simply copy and paste the contents of multiple .mock files and respect the JSON Array syntax. JSON Arrays allow to group any number of mocks into a single definition allowing to define more complex scenarios.

The construct is relatively straight forward, in that each JSON object contains two elements.

  • The first element “DataAccess” should be a reference to the Business Entity DataAccess class.

  • The second element “Data” should be a reference to the actual Data File.

Mock Data File

<?xml version="1.0"?> <dsCustomer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <eCustomer> <CustNum>1</CustNum> <Country>USA</Country> <Name>Lift line skiiing Ltd</Name> <Address>Unter Käster 1</Address> <Address2/> <City>Köln</City> <State>MA</State> <PostalCode>01730</PostalCode> <Contact>Gloria Shepley</Contact> <Phone>(617) 450-0086</Phone> <SalesRep>HXM</SalesRep> <CreditLimit>66700.0</CreditLimit> <Balance>903.64</Balance> <Terms>Net30</Terms> <Discount>35</Discount> <Comments>27/09/2017 20:23:12,627+02:00</Comments> <Fax/> <EmailAddress>info@lift-tours.com</EmailAddress> <Flags/> <SmartRecordKey>000000001</SmartRecordKey> <SmartAttachments>true</SmartAttachments> <SmartComments>false</SmartComments> <SmartCopiedFrom/> <eSalesrep> <SalesRep>HXM</SalesRep> <RepName>Harry Munvig 333</RepName> <Region>West</Region> <MonthQuota>1231</MonthQuota> <MonthQuota02>1</MonthQuota02> <MonthQuota03>4031</MonthQuota03> <MonthQuota04>4152</MonthQuota04> <MonthQuota05>4277</MonthQuota05> <MonthQuota06>4405</MonthQuota06> <MonthQuota07>4537</MonthQuota07> <MonthQuota08>4673</MonthQuota08> <MonthQuota09>4813</MonthQuota09> <MonthQuota10>4957</MonthQuota10> <MonthQuota11>5106</MonthQuota11> <MonthQuota12>5259</MonthQuota12>

The Mock Data File is an ‘xml’ file definition of the actual data to be loaded for the Mock Data Unit Testing. It is expected that developers modify the mock data file to build good, bad and extreme data sets for various test scenarios.

How to implement Mock Data within Unit Testing

The implementation of Mock Data is pretty straight forward.

Within your Unit Test Case Class, ensure you have the following ‘Using’ statement

USING Consultingwerk.SmartUnit.OERA.MockDataAccess.* FROM PROPATH.

The physical implementation of Mock Data is utilising the class “Consultingwerk.SmartUnit.OERA.MockDataAccess.MockDataAccessFactory”. The first task is to load the actual mock data:

MockDataAccessFactory:InitializeFromFile (“…DataAccess.mock”:U).

The above statement instantiates and loads the data into the defined Mock Data Access class. It is from this point that we are now able perform out Unit Test code, which shall use the data from our generated Mock Data.

The other method that you should be aware of is:

MockDataAccessFactory:ShutDown ( ).

This method is responsible for the clean-up operations and should always be run.

 

Below are some simple examples of Unit Test Methods utilising Mock Data:

Example 1: FetchCustomers

@Test. METHOD PUBLIC VOID TestFetchCustomer (): DEFINE VARIABLE oCustomer AS CustomerDatasetModel NO-UNDO . MockDataAccessFactory:InitializeFromFile ("abc/UnitTests/myModule/CustomerDataAccess.mock ":U) . oCustomer = NEW CustomerDatasetModel (2) . BufferAssert:IsAvailable(oCustomer:Customer) . Assert:Equals(2, oCustomer:Customer:CustNum) . FINALLY: MockDataAccessFactory:Shutdown() . END FINALLY. END METHOD.

Example 2: Fetch Customers 2

@Test. METHOD PUBLIC VOID TestFetchCustomer2 (): DEFINE VARIABLE hDataset AS HANDLE NO-UNDO. MockDataAccessFactory:InitializeFromFile ("abc/UnitTests/myModule/CustomerDataAccess.mock ":U) . ServiceInterface:FetchData ("Consultingwerk.SmartComponentsDemo.OERA.Sports2000.CustomerBusinessEntity", NEW FetchDataRequest ("eCustomer", "for each eCustomer", 100), OUTPUT DATASET-HANDLE hDataset) . Assert:Equals(100, BufferHelper:NumRecords(hDataset::eCustomer:HANDLE)) . FINALLY: MockDataAccessFactory:Shutdown() . END FINALLY. END METHOD.

Example 3: Update Customer

@Test. METHOD PUBLIC VOID TestUpdateCustomer (): DEFINE VARIABLE oCustomer AS CustomerDatasetModel NO-UNDO . MockDataAccessFactory:InitializeFromFile ("abc/UnitTests/myModule/CustomerDataAccess.mock ":U) . oCustomer = NEW CustomerDatasetModel (1) . oCustomer:TrackingChanges = TRUE . oCustomer:Customer:Name = "Unit Test Update". oCustomer:SaveChanges(). oCustomer = NEW CustomerDatasetModel () . oCustomer:Customer:Filter:Name:EQ ("Unit Test Update "):Run () . BufferAssert:IsAvailable(oCustomer:Customer) . FINALLY: MockDataAccessFactory:Shutdown() . END FINALLY. END METHOD.

Example 4: Delete Customer

@Test. METHOD PUBLIC VOID TestDeleteCustomer (): DEFINE VARIABLE oCustomer AS CustomerDatasetModel NO-UNDO . MockDataAccessFactory:InitializeFromFile ("abc/UnitTests/myModule/CustomerDataAccess.mock ":U) . oCustomer = NEW CustomerDatasetModel (1) . Assert:Equals(1, BufferHelper:NumRecords(oCustomer:DatasetHandle::eCustomer:HANDLE)) . oCustomer:TrackingChanges = TRUE . oCustomer:Customer:Delete () . oCustomer:SaveChanges(). FINALLY: GarbageCollector:DeleteObject (oCustomer) . MockDataAccessFactory:Shutdown() . END FINALLY. END METHOD.

Example 5: Test Delete and Count

@Test. METHOD PUBLIC VOID TestDeleteAndCountCustomer (): DEFINE VARIABLE oCustomer AS CustomerDatasetModel NO-UNDO . MockDataAccessFactory:InitializeFromFile ("abc/UnitTests/myModule/CustomerDataAccess.mock ":U) . oCustomer = NEW CustomerDatasetModel () . oCustomer:BatchSize = 100. oCustomer:Customer:Fill () . Assert:Equals(100, BufferHelper:NumRecords(oCustomer:DatasetHandle::eCustomer:HANDLE)) . oCustomer = NEW CustomerDatasetModel (2) . oCustomer:TrackingChanges = TRUE . oCustomer:Customer:Delete () . oCustomer:SaveChanges(). oCustomer = NEW CustomerDatasetModel (4) . oCustomer:TrackingChanges = TRUE . oCustomer:Customer:Delete () . oCustomer:SaveChanges(). oCustomer = NEW CustomerDatasetModel () . oCustomer:Customer:Fill() . Assert:Equals(98, BufferHelper:NumRecords(oCustomer:DatasetHandle::eCustomer:HANDLE)) . DEFINE VARIABLE oParameter AS CountRecordsRequest NO-UNDO . DEFINE VARIABLE hDataset AS HANDLE NO-UNDO . oParameter = NEW CountRecordsRequest ("eCustomer", "for each eCustomer") . ServiceInterface:InvokeMethod("Consultingwerk.SmartComponentsDemo.OERA.Sports2000.CustomerBusinessEntity", "CountResultRecords", INPUT-OUTPUT DATASET-HANDLE hDataset, oParameter) . Assert:Equals(98, oParameter:NumResults) . FINALLY: IF VALID-HANDLE (hDataset) THEN DELETE OBJECT hDataset . MockDataAccessFactory:Shutdown() . END FINALLY. END METHOD.