This post was initially going to be about refactoring a mail wrapper class that I came across, but there was a slight detour so today I will just write about adding a test for this class.

Here is a reproduction of the original code with some hardcoded text changed to protect the innocent:

public static class MailWrapper
{
    public static Task sendMessage(String subj, String content)
    {
        var msg = new SendGridMessage();

        var mailUser = "some@email.com"; // ConfigurationManager.AppSettings["emailFrom"];
        msg.SetFrom(new EmailAddress(mailUser, "site mailer"));

        var recipients = new List
        {
            new EmailAddress("some.other@email.com", "Recipient")
        };
        msg.AddTos(recipients);

        msg.SetSubject(subj);

        msg.AddContent(MimeType.Html, content);

        var apiKey = "someApiKey"; // ConfigurationManager.AppSettings["SENDGRID_APIKEY"];
        var client = new SendGridClient(apiKey);

        return client.SendEmailAsync(msg);
    }
}

This method is then called from an MVC controller:

[HttpPost]
public async Task Index(ContactModel cm)
{
    if (ModelState.IsValid)
    {
        var msg = "Username: " + cm.UserName +
            "<br /> Email: " + cm.Email +
            "<br /> Message: " + cm.Message;
        await MailWrapper.sendMessage("Message from the contact form", msg);
        return View("MailSent");
    }
    return View();
}

There are some issues with this code:

  • Hardcoded (and sensitive) strings
  • The MailWrapper class is in the same project as the MVC code
  • No test code was written
  • The code can be cleaner and more readable
  • The return statement of the sendMessage method appears to return a Response object, but the method signature just returns Task

There is also one good thing which is very important, and that is that the controller is not coupled directly with SendGrid. In this case, if we want to change the third-party library in the future, then there is only one place where this needs to be modified.

So I’ll start first by creating a test. When looking at the code, there doesn’t seem to be enough of an argument at the moment to write a unit test because what the code is mostly doing is calling a third-party library. So in this case, we’ll go with an integration test. This will ensure that when running the tests after a code change or a third-party library update, we will know that sending emails still works.

public class MailWrapperTest
{
    [Fact]
    public async Task Can_Send_A_Message()
    {
        var response = await MailWrapper.sendMessage(
            "This is a test", 
            "Feel free to delete the message as soon as you read it.");

        response.StatusCode.Should().Be(HttpStatusCode.Accepted);
    }
}

I have also decided to change the signature of the sendMessage method so that we can now also check the response:

public static Task sendMessage(String subj, String content)

There is a little side-effect when running the code, as there will be a mail sent to the selected recipient. In a future version, we can also check if this email was successfully received.

It’s always a good thing to get free recommendations from experts, and Azure Advisor is one such good thing.

When clicking on the Advisor tab, you will be presented with a dashboard. Give it some time to load, and you will be shown your personalised recommendations split into four categories.

An overview of Advisor recommendations

The categories are availability, security, performance, and cost. One example of each, starting with availability is enabling soft delete of blobs on storage accounts so that any accidental deletion can be recovered. An example of security is to enable secure transfer, again on storage accounts, to force all requests to be downloaded via HTTPS. For performance, Advisor can suggest optimal cache sizes and database optimisation. Finally, for costs, the tool can detect underutilised VMs and propose to switch them off or scale them down.

Often there is minimal friction in order to implement the recommended fix, providing you with details about the tip and a list of steps. Sometimes Advisor will happily do it for you if you decide to go for it. Let’s try one of the suggestions, I will go for high availability.

high availability summary

Here I am being told that creating an Azure Service Health alert will notify me of any server issues that affect my resources. This has a low impact because it will not result in any changes to the resources themselves. Let’s drill down further into the recommendation.

recommendation details

Here I have a short description of the benefits of rolling out this change, and a link to more detailed information. I also have the option to postpone this recommendation for a day, week, month, or quarter, or just dismiss so that I don’t get notified again with this suggestion. Most importantly, I have the option to create an Azure service health alert.

I’ll just fill in the required details so I get notified of service interruptions concerning any resources related to my website. The events that can trigger an alert are service issue, planned maintenance and health advisories. I can choose to be notified for any combination of them.

creating an alert rule

Then it’s just a matter of clicking the create button and we're good to go.

This post it the conclusion to a series. Make sure you alse read Part 1 and Part 2.

It’s time to concentrate on the implementation of our Outlet class. We will add a new private property where we set the time zones of each company.

private readonly string[] timezones =
{
    "Pacific Standard Time",
    "Central Standard Time",
};

This information would be fetched from a data store in practice, and you would do well to use an interface as well so it can be mocked. But I will leave this part for you as an exercise.

The code for fetching the current date and time is now as follows.

public string GetLocalDateTime(long id)
{
    var tz = TimeZoneInfo.FindSystemTimeZoneById(timezones[id]);
    var currentDate = dateTimeWrapper.Now();
    var offset = tz.GetUtcOffset(currentDate);
    var outletDate = currentDate.AddMinutes(offset.TotalMinutes);

    return outletDate.ToString("M/d/yyyy HH:mm");
}

So this method calculates the selected company’s timezone first through FindSystemTimeZoneById, and then reads the date and time from the wrapper we created in part 2. We continue by getting the timezone offset for this particular date by means of GetUtcOffset. This step is required because the offset is different between winter and summer due to daylight saving. The final step is to add this offset to the date (note also that it is in minutes because some time zones vay by half-hour steps) and return it as a string.

We did the change with confidence, and can now look at the test results:

All the tests are passing

We have now observed how code that interacts with the hosting system should be written. Whilst using the operating system’s resources is essential in a software program, having indirect access to those resources ensures that your code will be easier to test independently of any environment that it will run on.

Now that you know the basic concept, I have to reveal that there is a library that already does this. This popular library is NodaTime, which the authors describe as a better date and time API for .NET. NodaTime exposes everything through the ICock interface and even provides a mocking solution in the form of FakeClock.

Back in part 1 I jokingly closed with a test passing, but I was cheating a bit by building a method that returns a hardcoded string containing the expected result. This is perfectly acceptable in the initial phase to make a test pass, if only to be sure that the rest of the scaffolding was done correctly.

While driving home that day in the style of Papa Pig, an idea starts forming. What if we write another test to check for a second case? Back in the office the next day we add this test case for the fictional outlet with an id of 2. We need to change the test method into a parametrised test method:

[Theory]
[InlineData(0, "4/28/2019 10:00")]
[InlineData(1, "4/28/2019 12:00")]
public void Outlet_can_show_the_time_according_to_its_local_timezone(
    int outletId, string expectedResult)
{
    var result = sut.GetLocalDateTime(outletId);
    result.Should().Be(expectedResult);
}

In this way we may use the same test for multiple values. If we run the tests only one is passing, so we have a problem once more.

Only one of the two tests is passing

Let’s solve the problem properly this time. What we need is another layer of indirection. The Outlet class was directly dependant on the System.DateTime class, and with our change we only made it dependant on the DateTimeWrapper class. We still cannot control the behaviour of the date within our test. We need to add an interface that specifies the wrapper’s behaviour and then we will be able to mock it during the test execution. Visual Studio has a nice refactoring tool that allows us to quickly generate the interface:

Extracting the DateTimeWrapper Interface

It even allows us to choose the name and which methods to include in the interface:

We also have some control when extracting the interface

To be honest, I'm not sure if this feature is standard in all versions of Visual Studio, but here is the generated code anyway:

using System;

namespace TimeZoneHelper
{
    public interface IDateTimeWrapper
    {
        DateTime Now();
    }
}

That’s it, and the DateTimeWrapper class now also inherits the IDateTimeWrapper interface. Let’s use it! In the Outlet class change the type to IDateTimeWrapper:

public class Outlet
{
    private readonly IDateTimeWrapper dateTimeWrapper;

    public Outlet(IDateTimeWrapper dateTimeWrapper)
    {
        this.dateTimeWrapper = dateTimeWrapper;
    }

    public string GetLocalDateTime(long id)
    {
        return dateTimeWrapper.Now().ToString("M/d/yyyy HH:mm");
    }
}

Now any object that implements IDateTimeWrapper can be used with the Outlet class. Note that the choice for initialising the required wrapper class will be handled by the dependency injection middleware, but we won’t go into it here as we will just focus on the test.

In the test class, we will also use the interface, but on top of that we will mock it as well. It’s not that we want to make fun of it, we just want to be able to control what happens during the test. We will now use NSubstitute, which makes the task of creating mocks easier. Let us change the code inside the test constructor:

public OutletTest()
{
    var wrapper = Substitute.For<IDateTimeWrapper>();

    sut = new Outlet(wrapper);
}

We are still not specifying the behavior of the wrapper, so both tests are failing. Let’s add this behaviour, so now we can specify what the date is every time we run the test. With NSubstitute again, the code is nice and readable:

public OutletTest()
{
    var wrapper = Substitute.For<IDateTimeWrapper>();

    wrapper.Now().Returns(new DateTime(2019, 4, 28, 17, 00, 00, DateTimeKind.Utc));

    sut = new Outlet(wrapper);
}

So we have specified that every time we run the test, the UTC time is always April 28th 2019 at 17:00. We have gone through all this trouble but still the tests fail. That is because we did not implement any logic in the Outlet class, but now we have done enough groundwork to let us do that efficiently. The main point I wanted to show you was about removing hard dependencies to system objects and now we have achieved that. See you in part 3!

This post is based on a short talk I gave some months ago. Let’s say we have a client with a website hosted in one location in Azure. This client has multiple sub-domains associated with physical locations spread across the US. And there is a requirement that each sub-domain displays the time according to the timezone of its associated outlet.

Image source: http://time-time.net/times/time-zones/usa-canada/usa-time-zone-map.php

Let’s start by creating a new demo solution using the following steps in VS2019:

Create a new class library. I named my solution TimeZoneHelper. Then add a new xUnit test project to the solution

Adding a test project to the solution

On the test project, right click and choose ‘Manage NuGet packages’. Install the NSubstitute and FluentAssertions packages.

Installation of the required nuget packages for testing

We also need to reference the class library from the test project:

Referencing the base project

In the TimeZoneHelper project, add a new class named Outlet.cs with the following code:

using System;

namespace TimeZoneHelper
{
    public class Outlet
    {
        public string GetLocalDateTime(long id)
        {
            throw new NotImplementedException();
        }
    }
}

Note that I want to keep things as simple as possible to follow. In a real-life scenario, I would probably use a property to hold the id of the outlet rather than passing it around and keep the return type as DateTime with the proper timezone offset. The GetLocalDateTime method just throws an exception. We need to do this otherwise the solution will not compile. Otherwise, if the solution does not compile, we cannot test it. So let’s write the first test.

using FluentAssertions;
using TimeZoneHelper;
using Xunit;

namespace TimeZoneHelperTest
{
    public class OutletTest
    {
        private readonly Outlet sut;

        public OutletTest()
        {
            sut = new Outlet();
        }

        [Fact]
        public void Outlet_can_show_the_time_according_to_its_local_timezone()
        {
            var result = sut.GetLocalDateTime(1);
            result.Should().Be("4/28/2019 17:00");
        }
    }
}

In this test, we are initialising the Outlet class and calling its method to check if we get back the result that we wanted. Two things to note. As a convention, I usually use sut (system under test) or target for the variable that is used to hold the class to be tested. Second, note that by using FluentAssertions the test assertion is a very readable, i.e. result.Should().Be(...) rather than Assert.Equal(...).

Initial failing test

As expected, the test fails because we’re just throwing an exception. So let’s change that line of code with this:

public string GetLocalDateTime(long id)
{
    return DateTime.UtcNow.ToString("M/d/yyyy HH:mm");
}

Running the test, we see a different result, but it still fails:

Test still failing

The reason now is that I am expecting to get a particular point in time, but my current time is different. So this test will only be successful at exactly one point in the history of the world which is not very useful. Otherwise, I would have to change the expected value every time that I run the test, which is not very useful either. And I’ve seen people do this, but we won’t, so don’t even think about it.

The Outlet class is reading the current system’s date and time directly from the System object. Let’s try to uncouple the dependency between the two by introducing another object. Enter DateTimeWrapper.cs:

using System;

namespace TimeZoneHelper
{
    public class DateTimeWrapper
    {
        public DateTime Now()
        {
            return DateTime.UtcNow;
        }
    }
}

The Outlet class can now be modified to use the DateTimeWrapper:

public class Outlet
{
    private readonly DateTimeWrapper dateTimeWrapper;

    public Outlet(DateTimeWrapper dateTimeWrapper)
    {
        this.dateTimeWrapper = dateTimeWrapper;
    }

    public string GetLocalDateTime(long id)
    {
        return dateTimeWrapper.Now().ToString("M/d/yyyy HH:mm");
    }
}

In this way, the Outlet object does not need to be concerned with how the date is fetched and this responsibility is shifted to the DateTimeWrapper. If we need to change how the date is fetched in the future, perhaps because we found a better way to do it, then we won’t need to touch the Outlet class, which is good. However, at this point the logic is mostly the same as before, so the test still fails with the same outcome. Note that the test constructor now needs to initialise the DateTimeWrapper as well:

public OutletTest()
{
    sut = new Outlet(new DateTimeWrapper());
}

Now is the time that I would typically start cheating. Let’s modify the Now method in the DateTimeWrapper:

public DateTime Now()
{
    return new DateTime(2019, 4, 28, 17, 00, 00);
}

With this change, the test passes.

Test is passing!

We have done our job and we can all go home to rest. We may also brag about our cool code to anyone we are lucky to meet on our way out of the office.

Did we solve our problem completely? Or will we face an unexpected setback? Find out in part 2.