TDD Test Sample

In this post I would like to demonstrate few of my TDD (Test Driven Development) skills!

TDD

I  started TDD during 2010 with a Product Development Company.  Thereafter I am a Big Fan & Advocate of TDD.

Following are the Properties & Advantages of TDD:

  1. In TDD we first write the Unit Test. Then we start with the actual Code Method.  As the Test Method evolves the Original Method evolves too.
  2. TDD ensures the code is released with enough testing.
  3. TDD is an Investment for Future
  4. TDD is essential for Products
  5. TDD gives the Flexibility for Refactoring, More Confident Deployments
  6. TDD is used mostly in the Backend side

Coding Test

We want to expose a service that calculates the total spend given a supplier ID.

public class SpendService

{

public SpendSummary GetTotalSpend(int supplierId) { … }

}

The business logic is quite straightforward: the total spend is the sum of all the invoices, grouped by year. However, some of the suppliers are working with a separate branch of the company, which has its own invoicing platform.

Therefore, the flow should work as follows:

1) A SupplierService returns supplier data, which can be used to understand whether a supplier is external or not

2) If the supplier is not external, invoice data can be retrieved through the InvoiceRepository class

3) If the supplier is external, invoice data can be retrieved through the ExternalInvoiceService class

4) ExternalInvoiceService invokes a separate system, which might fail. However, data from this system is regularly backed up in a failover storage. A FailoverInvoiceService class gives access to that storage. It is ok to return failover data when ExternalInvoiceService fails.

5) Failover data might be not fresh. A timestamp property indicates when it has been originally stored. If this date is older than a month, it means that it has not been refreshed. In this case, the GetTotalSpend method should fail.

6) When ExternalInvoiceService is offline, usually calls tend to timeout, which means that the method takes long to complete. Therefore, after 3 consecutive errors, we want to bypass ExternalInvoiceService and go to FailoverInvoiceService directly, with the same logic as before. After 1 minute, we can try to re-enable ExternalInvoiceService again.

Solution

[TestClass]
    public class SpendServiceTest
    {
        private UnityContainer container;

        [TestInitialize]
        public void TestInit()
        {
            container = new UnityContainer();
            container.RegisterType<ISupplierDataService, SupplierDataServiceStub>();
            container.RegisterType<ISupplierService, SupplierService>();
            container.RegisterType<IInvoiceRepository, InvoiceRepositoryStub>();
            container.RegisterType<IExternalSpendService, ExternalInvoiceServiceStub>();
            container.RegisterType<ICircuitBreaker, ExternalSpendServiceInvoker>();
            container.RegisterType<IFailoverInvoiceService, FailoverInvoiceServiceStub>();
        }

        [TestMethod]
        public void SpendService_InternalCustomer_Name_Test()
        {
            // Arrange
            int supplierId = 1;
            SpendService spendService = container.Resolve<InternalSpendService>();

            // Act
            SpendSummary result = spendService.GetTotalSpend(supplierId);

            // Assert
            Assert.IsNotNull(result);
            Assert.AreEqual(“Supplier Internal”, result.Name);
        }

        [TestMethod]
        public void SpendService_InternalCustomer_Spend_Test()
        {
            // Arrange
            int supplierId = 1;
            SpendService spendService = container.Resolve<InternalSpendService>();

            // Act
            SpendSummary result = spendService.GetTotalSpend(supplierId);

            // Assert
            Assert.IsNotNull(result);
            Assert.AreEqual(2, result.Years.Count);
            Assert.AreEqual(2000, result.Years[0].TotalSpend);
            Assert.AreEqual(1000, result.Years[1].TotalSpend);
        }

        [TestMethod]
        public void SpendService_ExternalCustomer_Name_Test()
        {
            // Arrange
            int supplierId = 2;
            SpendService spendService = container.Resolve<ExternalSpendService>();

            // Act
            SpendSummary result = spendService.GetTotalSpend(supplierId);

            // Assert
            Assert.IsNotNull(result);
            Assert.AreEqual(“Supplier External”, result.Name);
        }

}

Following is Circuit Breaker.

public interface ICircuitBreaker
{
    int MaxTries { get; }

    int ClosedTimeSeconds { get; }

    int ObsoleteDaysLimit { get; }

    bool IsOpenState { get; set; }

    DateTime FailedTimestamp { get; set; }

    void Reset();

    List<SpendDetail> GetSpendDetail(int supplierId);
}

One Stub Sample.

public class FailoverInvoiceServiceStub : IFailoverInvoiceService
    {
        public FailoverInvoiceCollection GetInvoices(int supplierId)
        {
            FailoverInvoiceCollection result = new FailoverInvoiceCollection();

            if (supplierId == 2)
            {
                IList<ExternalInvoice> list = new List<ExternalInvoice>();
                list.Add(new ExternalInvoice() { Year = 2018, TotalAmount = 900 }); // Only 1 Year
                result.Timestamp = DateTime.Now;
                result.Invoices = list.ToArray();
            }
            else if (supplierId == 3)
            {
                IList<ExternalInvoice> list = new List<ExternalInvoice>();
                list.Add(new ExternalInvoice() { Year = 2018, TotalAmount = 900 }); // Only 1 Year
                result.Timestamp = DateTime.Now;
                result.Invoices = list.ToArray();
            }
            else if (supplierId == 4)
            {
                IList<ExternalInvoice> list = new List<ExternalInvoice>();
                list.Add(new ExternalInvoice() { Year = 2017, TotalAmount = 800 });
                result.Timestamp = DateTime.Now.AddDays(-32); // Obsolete Date
                result.Invoices = list.ToArray();
            }

            return result;
        }
    }

Strategies

I have been using following Frameworks & Strategies:

  1. Unit Test created using C#
  2. Moq Framework for Mocking
  3. Stubbing using C# Classes
  4. Wrapper Methods for Static Methods unit testing

References

You can find the Code in GitHub.

https://github.com/jeanpaulva/jp-tdd

Summary

This is a TDD Code Demonstration to showcase my skills. 

Thank you All!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s