I'm not experienced with testing, therefore I'd like you to look at my code proposal of unit test. My solution has following projects
- Tests\Application.Tests
- Application
- Domain
- Infrastructure
- Web
And I am not sure with Application Service Test. The tested class is DeliveryDateService
namespace Application.Services
{
public class DeliveryDateService : IDeliveryDateService
{
private readonly IHttpContextAccessor _httpAccessor;
private readonly IDeliveryDateRepository _repository;
public DeliveryDateService(IHttpContextAccessor httpAccessor, IDeliveryDateRepository repository)
{
_httpAccessor = httpAccessor;
_repository = repository;
}
public void Handle(Import cmd)
{
var deliveryDates = new List<DeliveryDate>();
foreach (DeliveryDateDto item in cmd.Items)
{
deliveryDates.Add(new DeliveryDate(
id: null,
customerNumber: item.CustomerNumber,
date: item.Date,
modifiedBy: _httpAccessor.HttpContext.User.Identity.Name,
modifiedOn: DateTime.Now
));
}
_repository.Import(deliveryDates);
}
}
}
The real repository use entity framework to save data into database. But for the test the idea is that I will use Fake Repository, which will contains data only in memory. Therefore I am not testing the repositories, but only the service itself.
namespace Application.Tests.Fakes
{
public class DeliveryDateRepositoryFake : IDeliveryDateRepository
{
private List<DeliveryDate> Dates = new List<DeliveryDate>();
public List<DeliveryDate> FindAll()
{
return Dates;
}
public void Import(IList<DeliveryDate> dates)
{
Truncate();
foreach (var date in dates)
{
Dates.Add(date);
}
}
public void Truncate()
{
foreach (var date in Dates)
{
Dates.Remove(date);
}
}
}
}
And the test looks like
namespace Application.Tests.Services
{
public class DeliveryDateServiceTests
{
private DeliveryDateRepositoryFake _repository;
private IDeliveryDateService _service;
private void Setup()
{
_repository = new DeliveryDateRepositoryFake();
_service = new DeliveryDateService(
MockHelper.MockIHttpContextAccessor().Object,
_repository);
}
private void Cleanup()
{
_repository = null;
_service = null;
}
[Fact]
public void Import()
{
Setup();
var items = new List<DeliveryDateDto>
{
new DeliveryDateDto(customerNumber: 123456, date: DateTime.Now),
new DeliveryDateDto(customerNumber: 123457, date: DateTime.Now)
};
_service.Handle(new Commands.DeliveryDate.Import(items));
Assert.Equal(
expected: items.Count,
actual: _repository.FindAll().Count);
Cleanup();
}
}
}
Does it make sense, can it be improved ? I read, that Setup and Cleanup methods are not ideal, but also I don't want to repeat the code. Also I have to write Fake Repository for each Real Repository, it's extra work, but it's not complicated yet and it looks like it might work well.
Assertions (updated)
I added some assertions also on items data
// Assertions
var actualCustomerOne = items.ToArray()[0];
var actualCustomerTwo = items.ToArray()[1];
var expectedCustomerOne = _repository.FindAll().ToArray()[0];
var expectedCustomerTwo = _repository.FindAll().ToArray()[1];
Assert.Equal(items.Count, _repository.FindAll().Count);
Assert.Equal(actualCustomerOne.CustomerNumber, expectedCustomerOne.CustomerNumber);
Assert.Equal(actualCustomerOne.Date, expectedCustomerOne.Date);
Assert.NotNull(expectedCustomerOne.ModifiedBy);
Assert.NotEqual(DateTime.MinValue, expectedCustomerOne.ModifiedOn);
Assert.Equal(actualCustomerTwo.CustomerNumber, expectedCustomerTwo.CustomerNumber);
Assert.Equal(actualCustomerTwo.Date, expectedCustomerTwo.Date);
Assert.NotNull(expectedCustomerTwo.ModifiedBy);
Assert.NotEqual(DateTime.MinValue, expectedCustomerTwo.ModifiedOn);