我正在尝试Moq同步过程,但我有一个特定部分的问题。
在我的方法中,我试图Moq我执行以下操作:
public class SyncManager
{
private IPubHttpClient _pubHttpClient;
private ILogService _logService;
private Ilogger _logger;
public SyncManager(IPubHttpClient pubClient, ILogService logService ILogger<SyncManager> logger)
{
_pubHttpClient = pubClient;
_logService = logService;
_logger = logger;
}
public async Task Sync()
{
var syncStatus = SyncStatus.Error;
// get logs
var logs = await _logService.GetLogs();
foreach (var log in logs)
{
if (!string.IsNullOrEmpty(log.CostCode))
syncStatus = await GetAndSendCost(log);
elseif
syncStatus = await GetAndSendSort(log);
}
}
private async Task<SyncStatus> GetAndSendCost(Log log)
{
var cost = new Cost
{
CostCode = log.CostCode,
CostName = log.Description,
Active = log.Active
};
await _pubHttpClient.Push(new EventModel { Cost = cost, MessageType = log.Type.GetDescription() });
return SyncStatus.Success;
}
private async Task<SyncStatus> GetAndSendSort(Log log)
{
var sort = new Sort
{
SortCode = log.SortCode,
SortName = log.Description,
Active = log.Active
};
await _pubHttpClient.Push(new EventModel { Sort = sort, MessageType = log.Type.GetDescription() });
return SyncStatus.Success;
}
}
public class Log
{
public long Id { get; set; }
public string SortCode { get; set; }
public string CostCode { get; set; }
public string Description { get; set; }
public string Active { get; set; }
public AuditType Type { get; set; }
}
public class EventModel
{
public Cost Cost { get; set; }
public Sort Sort { get; set; }
public string MessageType { get; set; }
}
public enum AuditType
{
[Description("CREATE")]
Create = 0,
[Description("UPDATE")]
Update = 1,
[Description("DELETE")]
Delete = 2
}
public static class EnumExtensions
{
public static string GetDescription(this Enum enumValue)
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DescriptionAttribute>()?
.Description ?? string.Empty;
}
}
我的测试设置如下:
public class SyncManagerTests
{
public readonly Mock<IPubHttpClient> _pubClientMock = new();
public readonly Mock<ILogService> _logServiceMock = new();
[Fact]
public async Task Should_Sync()
{
var mockedCost = new Cost { Active = Status.Active, CostCode = "0000", CostName = "UNIT TEST" };
var mockedSort = new Sort { Active = Status.Active, SortCode = "0001", SortName = "UNIT TEST" };
var mockedLogs = new List<Log> {
new Log { CostCode = mockedCost.CostCode, Description = mockedCost.CostName, Active = mockedCost.Active, Id = 1 },
new Log { SortCode = mockedSort.SortCode, Description = mockedSort.CostName, Active = mockedSort.Active, Id = 2 },
};
_logServiceMock.Setup(s => s.GetLogs()).ReturnsAsync(mockedLogs).Verifiable();
_pubClientMock.Setup(p => p.Push(It.Is<EventModel>(x => x.Cost == mockedCost && x.MessageType == "CREATE"))).Returns(Task.CompletedTask).Verifiable();
var syncManager = new SyncManager(_pubClientMock.Object, _logServiceMock.Object, Mock.Of<ILogger<SyncManager>>());
await syncManager.Sync();
_pubClientMock.Verify(p => p.Push(It.Is<EventModel>(
x => x.Cost.CostName == mockedCost.CostName
&& x.Cost.CostCode == mockedCost.CostCode
&& x.Cost.Active == mockedCost.Active
&& x.MessageType == "CREATE")));
}
}
当我运行这个测试时,每段代码都被正确调用,在调试时,我看到EventModel object
是用正确的值创建的。
然而,在我的测试中,当我调用_pubClientMock.Verify();
时,我得到System.NullReferenceException
:这里的x.Cost
似乎是NULL。
知道为什么这个属性是NULL或者我在这里做错了什么吗?
所以要再次迭代,实际上调用.Sync()
并使用调试器分步执行代码可以完美地工作。这是_pubClientMock.Verify
失败与NullReferenceException
。
与已经提供的答案之一类似,我建议通过使用It.IsAny
来放松设置以避免引用相等的问题。这将允许测试用例顺利完成。
至于Verification中的null引用错误,因为更新后的示例会出现这样的情况:其中一个事件模型的Cost
属性值为空,因此在验证谓词
[TestClass]
public class SyncManagerTests {
public readonly Mock<IPubHttpClient> _pubClientMock = new Mock<IPubHttpClient>();
public readonly Mock<ILogService> _logServiceMock = new Mock<ILogService>();
[Fact]
public async Task Should_Sync() {
var mockedCost = new Cost { Active = Status.Active, CostCode = "0000", CostName = "UNIT TEST" };
var mockedSort = new Sort { Active = Status.Active, SortCode = "0001", SortName = "UNIT TEST" };
var mockedLogs = new List<Log> {
new Log { CostCode = mockedCost.CostCode, Description = mockedCost.CostName, Active = mockedCost.Active, Id = 1 },
new Log { SortCode = mockedSort.SortCode, Description = mockedSort.SortName, Active = mockedSort.Active, Id = 2 },
};
_logServiceMock.Setup(s => s.GetLogs()).ReturnsAsync(mockedLogs);
_pubClientMock
.Setup(p => p.Push(It.IsAny<EventModel>())) //<-- NOTE THIS
.Returns(Task.CompletedTask);
var syncManager = new SyncManager(_pubClientMock.Object, _logServiceMock.Object, Mock.Of<ILogger<SyncManager>>());
await syncManager.Sync();
_pubClientMock.Verify(p => p.Push(It.Is<EventModel>(
x => x.Cost != null //<-- NOTE THE NULL CHECK
&& x.Cost.CostName == mockedCost.CostName
&& x.Cost.CostCode == mockedCost.CostCode
&& x.Cost.Active == mockedCost.Active
&& x.MessageType == "CREATE")));
}
}
你能提供更多关于日志你通过GetAndSendCost()也许问题来自那部分,你可以使用它。isany
// Arrange
Mock<IPubHttpClient> _pubClientMock = new Mock<IPubHttpClient>();
_pubClientMock.Setup(p => p.Push(It.IsAny<EventModel>())).Returns(Task.CompletedTask).Verifiable();
var syncManager = new SyncManager(_pubClientMock.Object, Mock.Of<ILogger<SyncManager>>());
// Act
await syncManager.Sync();
// Assert
_pubClientMock.Verify(p => p.Push(It.Is<EventModel>(
x => x.Cost.CostName == mockedCost.CostName
&& x.Cost.CostCode == mockedCost.CostCode
&& x.Cost.Active == mockedCost.Active
&& x.MessageType == "CREATE")));