Arranging test seed data using C# Generics and Method Chaining

125 views Asked by At

I am a tester with a bit of coding experience. I have to write a heap of automated tests, testing an existing legacy business layer. There are around 1000 business logic classes, grouped roughly into about 30 logical modules, although the business classes can be referenced by any module's business logic, not just their own module.

The system's database (SQL Server) contains approx 800 tables, and is defined and updated using SQL scripts. For the tests I am using a backed-up, restored, truncated version of the full db which I load as an empty LocalDB for the tests. I then seed the LocalDB with required test data for each business class involved in the test - to do this I create a new business class object and call the business class's DoSave() function.

Some business classes will understandably be used very frequently in the tests (eg Client, Contract, Delivery, Product, Price etc). So I'd like to create a TestData class where I define a good sample list of these entities and reuse them in my tests as needed.

I'd also like to be able to utilise Method Chaining to make it simple for myself and other testers to find and load the specific entity(s) we are targeting from each class's list of records. And finally I was looking for a way to do this using Generics so I could implement the data seeding using generic code.

So I've tried two similar ways to achieve this but I couldn't find a way around some issues. I'm not sure its even possible, but I feel it should be.

A simplified Test case will look something like this. The call to DataBuilder<Client>() is to direct the code to return defined record(s). But I wish to access other class's records similarly with DataBuilder<Contract>(), or DataBuilder<Delivery>() etc, via Generics.

using System;
using Old.System.ClientModule;
using Xunit;

namespace Old.System.Client.Tests
{
    public class ClientTests
    {

        [Fact]
        public void ClientDoSave_ShouldCreateAClient_WhenValidClientDetailsSet()
        {
            // Arrange
            SetupLocalDBWithConnectionString(@"Server=(LocalDB)\MSSQLLocalDB;Integrated Security=true;AttachDbFileName=.\Path\To\LOCAL_DB.MDF");

            var client1 = new DataBuilder<Client>().First();

            var client2 = new Client();
            client2.ClientId = client.ClientId;

            // Act
            client1.DoSave();
            client2.DoRead();

            // Assert
            Assert.Equal(client1.ClientLongName, client2.ClientLongName);
        }

    }
}

These were my first attempt at DataBuilder and TestData - here I tried using DataSet = TestData.GetRecords<T>(); to define which GetRecords in TestData to call. But I can't overload GetRecords() by giving each one a unique <T>.

using System.Collections.Generic;

namespace Old.System.Tests
{
    public class DataBuilder<T>
    {
        private List<T> DataSet = new List<T>();

        public DataBuilder()
        {
            DataSet = TestData.GetRecords<T>();
        }

        // i know these chained methods aren't great - they're just here for display 

        public List<T> All()
        {
            return DataSet;
        }

        public T First()
        {
            return DataSet[0];
        }

        // many other (proper) chained methods

    }
}
using System.Collections.Generic;
using Old.System.ClientModule;

namespace Old.System.Tests
{
    static class TestData
    {

        public static List<Client> GetRecords<Old.System.ClientModule.Client>()
        {
            return new List<Client>
            {
                new Client { ClientId = "12345678", ClientShortName = "JohnJ", ClientLongName = "John Johnson" },
                new Client { ClientId = "23456789", ClientShortName = "PeterP", ClientLongName = "Peter Peterson" }
            };
        }

        public static List<Contract> GetRecords<Old.System.ClientModule.Contract>()
        {
            return new List<Contract>
            {
                new Contract { ContractId = "123" },
                new Contract { ContractId = "234" }
            };
        }

    }
}

This was my second attempt at DataBuilder and TestData - here I tried using passing some value to the overloaded TestData.GetRecords(T Entity) methods, but I can't work out what to send here. I know I can't send T (the Type), so I tried TestData.GetRecords(new T()), but that doesn't work.

using System.Collections.Generic;

namespace Old.System.Tests
{
    public class DataBuilder<T>
    {
        private List<T> DataSet = new List<T>();

        public DataBuilder()
        {
            // DataSet = TestData.GetRecords(T);
            // DataSet = TestData.GetRecords(new T());
            DataSet = TestData.GetRecords(???);
        }

        // i know these chained methods aren't great - they're just here for display 

        public List<T> All()
        {
            return DataSet;
        }

        public T First()
        {
            return DataSet[0];
        }

        // many other (proper) chained methods

    }
}
using System.Collections.Generic;
using Old.System.ClientModule;

namespace Old.System.Tests
{
    static class TestData
    {

        public static List<Client> GetRecords(Client Entity)
        {
            return new List<Client>
            {
                new Client { ClientId = "12345678", ClientShortName = "JohnJ", ClientLongName = "John Johnson" },
                new Client { ClientId = "23456789", ClientShortName = "PeterP", ClientLongName = "Peter Peterson" }
            };
        }

        public static List<Contract> GetRecords(Contract Entity)
        {
            return new List<Contract>
            {
                new Contract { ContractId = "123" },
                new Contract { ContractId = "234" }
            };
        }

    }
}

Please ignore the contents of the Act and Assert of the test, and the illogical aspect of the chained methods - I've just included what's here as an indication of what my intention is. My question is based around how to use Generics in this scenario. How do I call a type-specific TestData.GetRecords() for the Type that is passed into DataBuilder?

Any help is appreciated, but I'd really like to try to find a solution with this pattern, rather than resort to a Dictionary of Key=BusinessClass and Value=List<BusinessClass> of Records sort of solution.

0

There are 0 answers