0

I am trying to unit test an Api Controller that asynchronously sends an email to a user based on an HTML template.

The controller action:

    public async Task<IHttpActionResult> Email(int id)
    {
        var company = _unitOfWork.Companies.GetCompany();

        var repairTypes = _unitOfWork.RepairTypes.GetRepairTypes().ToList();

        var repair = _unitOfWork.Repairs.GetRepair(id);

        if (repair == null || company == null || !repairTypes.Any() || !repair.Devices.Any())
            return NotFound();

        if (repair.Customer.Email == null)
        {
            return BadRequest("No customer email");
        }
        try
        {
            var viewModel = new ReceiptViewModel(company, repair, false, false);

            viewModel.AssignRepairTypes(repairTypes);

            await Core.Utilities.Email
                .SendEmail(repair.Customer.Email, viewModel.GetReceiptEmailBody(), "Your Repair Summary");
        }
        catch (Exception exception)
        {
            return BadRequest(exception.Message);
        }
        return Ok();
    }

View model that reads the template from disk:

public class ReceiptViewModel
{
    public Company Company { get; set; }
    public Repair Repair { get; }
    public bool ShowLogo { get; private set; }
    public bool AutoPrint { get; private set; }

    public ReceiptViewModel(Company company, Repair repair, bool showLogo, bool autoPrint)
    {
        Company = company;
        Repair = repair;
        ShowLogo = showLogo;
        AutoPrint = autoPrint;

        if (Company.LogoPath.Contains("~"))
        {
            Company.LogoPath = Company.LogoPath.Remove(0, 1);
        }
    }

    public void AssignRepairTypes(IEnumerable<RepairType> repairTypes)
    {
        Repair.Devices.ForEach(d => d.RepairType =
            repairTypes.SingleOrDefault(rt => rt.Id == d.RepairTypeId));
    }

    public string GetReceiptEmailBody()
    {
        var server = HttpContext.Current.Server;

        // Generate the email body from the template file.
        var emailHtmlBody = new TemplateService();

        var path = server.MapPath("~/Views/Repairs/ReceiptTemplate.cshtml");

        var readAllText = File.ReadAllText(path);

        return emailHtmlBody.Parse(readAllText, this, null, "RepairEmail");
    }
}

And the test class:

[TestClass]
public class RepairsControllerTests
{
    private RepairsController _controller;
    private Mock<ICompanyRepository> _mockCompanyRepository;
    private Mock<IRepairRepository> _mockRepairRepository;
    private Mock<IRepairTypeRepository> _mockRepairTypeRepository;

    [ClassInitialize]
    public static void Init(TestContext context)
    {
        Mapper.Initialize(c => c.AddProfile<MappingProfile>());
    }

    [TestInitialize]
    public void TestInitialize()
    {

        HttpContext.Current = new HttpContext(new HttpRequest(null, "http://test.org", null), new HttpResponse(null));

        _mockCompanyRepository = new Mock<ICompanyRepository>();
        _mockRepairRepository = new Mock<IRepairRepository>();
        _mockRepairTypeRepository = new Mock<IRepairTypeRepository>();

        var mockUoW = new Mock<IUnitOfWork>();

        mockUoW.SetupGet(u => u.Companies).Returns(_mockCompanyRepository.Object);
        mockUoW.SetupGet(u => u.Repairs).Returns(_mockRepairRepository.Object);
        mockUoW.SetupGet(u => u.RepairTypes).Returns(_mockRepairTypeRepository.Object);

        _controller = new RepairsController(mockUoW.Object);

        _controller.MockCurrentUser("1", "user1@domain.com");
    }


//All other tests..

    [TestMethod]
    public async Task Email_ValidRequest_ShouldReturnOk()
    {
        var company = new Company() { Name = "Test", Email = "a@a.com", Id = 1, City = "T", Country = "C", LogoPath = "", PhoneNumber = "", PostCode = "", Street = "", TnC = "" };
        var repairTypes = new List<RepairType>() { new RepairType() { Id = 1, Name = "Test" } };
        var devices = new List<Device>() { new Device() { Id = 1, SerialNumber = "1", RepairTypeId = 1 } };
        var repair = new Repair
        {
            Id = 1,
            Customer = new Customer() { Email = "test@tm.com" },
            Devices = devices
        };

        _mockRepairRepository.Setup(r => r.GetRepair(1)).Returns(repair);
        _mockCompanyRepository.Setup(r => r.GetCompany()).Returns(company);
        _mockRepairTypeRepository.Setup(r => r.GetRepairTypes()).Returns(repairTypes);

        var result = await _controller.Email(1);
        result.Should().BeOfType<OkResult>();
    }
}

So the problem is that the Email_ValidRequest_ShouldReturnOk() test fails because Server.Mappath is returning null and therefore the template is not loaded. As you can see, I tried initialising the current HttpContext but Server.Mappath is still not working. I have also tried replacing the Server.Mappath call with Path.Combine, using the current app domain to determine the path. This also did not work as the app domain was pointing to Debug folder.

Having done some research on this matter, I realise that using Server.MapPath is not recommended as it introduces another dependency, which is very difficult to unit test.

I am very new to unit testing and clean architecture in general so any suggestions are greatly appreciated.

umutesen
  • 2,523
  • 1
  • 27
  • 41
  • 3
    you are not alone http://stackoverflow.com/questions/19563106/unit-testing-for-server-mappath – lordkain Nov 17 '16 at 12:59
  • Don't couple your code to HttpContext. abstract access to the functionality you want and use DI – Nkosi Nov 17 '16 at 13:15

0 Answers0