使用 NUnit 和 Moq 进行测试时意外的空引用
Unexpected null reference when testing with NUnit and Moq
我在 运行 进行测试时遇到空引用错误,但无法弄清楚。下面是我的测试
[Test]
[TestCase(...)]
public void Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
using (var _imprintDbContext = new ImprintDbContext(_dbContextOptions))
{
var mockExecRepo = new Mock<IExecutionReportRepository>();
mockExecRepo.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>())).Verifiable();//.Returns<Task<PagedResult<ExecutionReport>>>(x => null);
var uow = new Mock<IUnitOfWork>();
uow.SetupGet(x => x.ExecutionReports).Returns(mockExecRepo.Object);
var controller = new TradesController(uow.Object);
var query = new Query()
{
Field = field,
Operator = op,
Search = DateTime.Now.Add(TimeSpan.FromDays(dayManip)).Date.ToString("yyyy-MM-dd"),
SortDirection = sortDir
};
TradesController.TradesBody tb = new TradesController.TradesBody()
{
queries = new[] { query },
filterTerm = filterTerm
};
var results = controller.Get(tb, pageIndex, pageSize);
uow.Verify(mock => mock.ExecutionReports.GetFilteredTrades(new[] { query }, pageIndex, pageSize, filterTerm), Times.Once());
}
}
以及我正在模拟的一些对象的定义:
public interface IExecutionReportRepository : IRepository<ExecutionReport>
{
...
Task<IPagedResult<ExecutionReport>> GetFilteredTrades(IEnumerable<Query> queries, int pageIndex, int pageSize, string filterTerm);
}
工作单位:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context, IExecutionReportRepository executionReportRepository)
{
_context = context;
ExecutionReports = executionReportRepository;
}
public IExecutionReportRepository ExecutionReports { get; }
}
交易控制器:
public class TradesController : Controller
{
public class TradesBody
{
public IEnumerable<Query> queries;
public string filterTerm;
}
private readonly IUnitOfWork unitOfWork;
public TradesController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
/// <summary>
/// Gets a list of trades for the current date
/// </summary>
[HttpPost]
public async Task<IActionResult> Get([FromBody] TradesBody postBody, int pageIndex = 0, int pageSize = 100)
{
string filterTerm = postBody.filterTerm ?? "";
IEnumerable<Query> queries = postBody.queries;
IPagedResult<Domain.Entities.Core.ExecutionReport> queryResult;
queryResult = await unitOfWork.ExecutionReports.GetFilteredTrades(queries, pageIndex, pageSize, filterTerm); //Null reference error here
return Ok(queryResult);
}
}
单步执行代码时,我没有看到任何空对象,因此实际上无法 see/understand 在实际找到空引用的地方,但是我注意到在调试期间我看不到方法定义对于'GetFilteredTrades
。根据 this 判断,我的模拟方法没有连接到正在执行的方法,但是我只有一个 GetFilteredTrades
。
如何解决 TradesController
中抛出的空引用错误并成功 运行 我的测试?
您没有设置 GetFilteredTrades
到 return 任何东西,所以当您尝试等待它时它失败了。
mockExecRepo
.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
.ReturnAsync(Mock.Of<IPagedResult<ExecutionReport>>()) //<--THIS
.Verifiable();
而且被测方法是异步的,所以测试也应该是异步的。
[Test]
[TestCase(...)]
public async Task Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
等待测试调用的方法
var results = await controller.Get(tb, pageIndex, pageSize);
最后,您正在根据设置验证错误的模拟。由于 mockExecRepo
设置有 Verifiable()
那么你可以简单地在 mock 上调用 Verify()
。
//...
//Act
var results = await controller.Get(tb, pageIndex, pageSize);
//Assert
mockExecRepo.Verify();
验证它是否按预期调用。
我在 运行 进行测试时遇到空引用错误,但无法弄清楚。下面是我的测试
[Test]
[TestCase(...)]
public void Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
using (var _imprintDbContext = new ImprintDbContext(_dbContextOptions))
{
var mockExecRepo = new Mock<IExecutionReportRepository>();
mockExecRepo.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>())).Verifiable();//.Returns<Task<PagedResult<ExecutionReport>>>(x => null);
var uow = new Mock<IUnitOfWork>();
uow.SetupGet(x => x.ExecutionReports).Returns(mockExecRepo.Object);
var controller = new TradesController(uow.Object);
var query = new Query()
{
Field = field,
Operator = op,
Search = DateTime.Now.Add(TimeSpan.FromDays(dayManip)).Date.ToString("yyyy-MM-dd"),
SortDirection = sortDir
};
TradesController.TradesBody tb = new TradesController.TradesBody()
{
queries = new[] { query },
filterTerm = filterTerm
};
var results = controller.Get(tb, pageIndex, pageSize);
uow.Verify(mock => mock.ExecutionReports.GetFilteredTrades(new[] { query }, pageIndex, pageSize, filterTerm), Times.Once());
}
}
以及我正在模拟的一些对象的定义:
public interface IExecutionReportRepository : IRepository<ExecutionReport>
{
...
Task<IPagedResult<ExecutionReport>> GetFilteredTrades(IEnumerable<Query> queries, int pageIndex, int pageSize, string filterTerm);
}
工作单位:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context, IExecutionReportRepository executionReportRepository)
{
_context = context;
ExecutionReports = executionReportRepository;
}
public IExecutionReportRepository ExecutionReports { get; }
}
交易控制器:
public class TradesController : Controller
{
public class TradesBody
{
public IEnumerable<Query> queries;
public string filterTerm;
}
private readonly IUnitOfWork unitOfWork;
public TradesController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
/// <summary>
/// Gets a list of trades for the current date
/// </summary>
[HttpPost]
public async Task<IActionResult> Get([FromBody] TradesBody postBody, int pageIndex = 0, int pageSize = 100)
{
string filterTerm = postBody.filterTerm ?? "";
IEnumerable<Query> queries = postBody.queries;
IPagedResult<Domain.Entities.Core.ExecutionReport> queryResult;
queryResult = await unitOfWork.ExecutionReports.GetFilteredTrades(queries, pageIndex, pageSize, filterTerm); //Null reference error here
return Ok(queryResult);
}
}
单步执行代码时,我没有看到任何空对象,因此实际上无法 see/understand 在实际找到空引用的地方,但是我注意到在调试期间我看不到方法定义对于'GetFilteredTrades
。根据 this 判断,我的模拟方法没有连接到正在执行的方法,但是我只有一个 GetFilteredTrades
。
如何解决 TradesController
中抛出的空引用错误并成功 运行 我的测试?
您没有设置 GetFilteredTrades
到 return 任何东西,所以当您尝试等待它时它失败了。
mockExecRepo
.Setup(mock => mock.GetFilteredTrades(It.IsAny<IEnumerable<Query>>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
.ReturnAsync(Mock.Of<IPagedResult<ExecutionReport>>()) //<--THIS
.Verifiable();
而且被测方法是异步的,所以测试也应该是异步的。
[Test]
[TestCase(...)]
public async Task Get_ShouldReturnTradesMock(string field, Operator op, int dayManip, SortDirection sortDir, string filterTerm, int pageIndex, int pageSize, int expectedRecordMinSize)
{
等待测试调用的方法
var results = await controller.Get(tb, pageIndex, pageSize);
最后,您正在根据设置验证错误的模拟。由于 mockExecRepo
设置有 Verifiable()
那么你可以简单地在 mock 上调用 Verify()
。
//...
//Act
var results = await controller.Get(tb, pageIndex, pageSize);
//Assert
mockExecRepo.Verify();
验证它是否按预期调用。