问题
I have an IUnitOfWork interface that encapsulates my custom repositories. My custom repositories in turn inherit from an IRepository interface.
// The class that I am attempting to unit test
// EmployeeBusiness.cs
private readonly IUnitOfWork _unitOfWork;
public EmployeeBusiness(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public EmployeeDto AddEmployee(EmployeeDto employeeDto)
{
var employee = Mapper.Map<Employee>(employeeDto);
if (employee == null) return null;
_unitOfWork.Employees
.Add(employee);
_unitOfWork.Complete();
return Mapper.Map<EmployeeDto>(employee);
}
// IUnitOfWork interface
public interface IUnitOfWork : IDisposable
{
IEmployeeRepository Employees { get; }
void Complete();
}
// IEmployeeRepository interface
public interface IEmployeeRepository : IRepository<Employee> { }
// IRepository<T> interface
public interface IRepository<TEntity> where TEntity : class
{
void Add(TEntity entity);
// I have added other methods for simplicity
}
I am struggling with unit testing the AddEmployee()
method because I am getting this error:
Expected invocation on the mock once, but was 0 times: uow => uow.Employees.Add(Employee) Configured setups: IUnitOfWork uow => uow.Employees.Add(Employee) Performed invocations: IRepository`1.Add(Employee)
This is my unit test
[SetUp]
public void SetUp()
{
_employeeDto = new EmployeeDto
{
FirstName = "John",
LastName = "Smith",
BirthDate = new DateTime(1965, 12, 31)
};
_employee = new Employee
{
FirstName = "John",
LastName = "Smith",
BirthDate = new DateTime(1965, 12, 31)
};
_unitOfWork = new Mock<IUnitOfWork>();
Mapper.Initialize(cfg =>
{
cfg.AddProfile<EmployeeProfile>();
});
}
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
_employeeBusiness.AddEmployee(_employeeDto);
_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once);
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
回答1:
The reason the test you provided in your answer (copied below) passes when your original fails is because your original was expecting the mock to be called with the specific reference variable you're using (in this case _employee). Perhaps you were expecting Moq and .Verify() to check for equality using .equals() instead of ==?
In the context of your method under test, this is correct and desirable - based on the name of the test you're just looking to test that the method does in fact map your input to an Employee and calls the repository's add method. If you want to make sure that data wasn't lost in the mapping, you could use It.Is(), which takes a function you can use to assert qualities of the input (such as the name matching your expected value).
If you're aiming just to test that the mapping succeeded, you may be interested in Automapper Configuration Validation as a separate test.
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
//_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once); <-- This did not work
_unitOfWork.Verify(uow => uow.Employees.Add(It.IsAny<Employee>()), Times.Once); // <-- After changing this to It.IsAny<Employee>() it worked
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
回答2:
I have managed to get it to work by changing my unit test
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
//_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once); <-- This did not work
_unitOfWork.Verify(uow => uow.Employees.Add(It.IsAny<Employee>()), Times.Once); // <-- After changing this to It.IsAny<Employee>() it worked
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
Can anyone please help me understand the difference of using It.IsAny<Employee>()
as opposed to the _employee
variable?
UPDATE
The explanation can be found at Thorin's answer.
回答3:
You aren't checking the return value of AddEmployee
.
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
Assert.IsNotNull(result); // <---
_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once);
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
来源:https://stackoverflow.com/questions/52854915/how-to-create-a-unit-test-for-adding-items-in-a-repository