简体   繁体   English

Moq:验证参数空引用中的对象

[英]Moq: Verify object in parameter null reference

I'm trying to Moq a synchronization process, but I'm having issues with one specific part.我正在尝试 Moq 一个同步过程,但我遇到了一个特定部分的问题。

In my method I'm trying to Moq I perform the following:在我的方法中,我尝试最小起订量,我执行以下操作:

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;
    }
}

My tests I have set up to like this:我设置的测试是这样的:

    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")));
        }
    }

When I run this test, every piece of code is called correctly and while debugging I see that the EventModel object is being created with the correct values.当我运行这个测试时,每段代码都被正确调用,并且在调试时我看到EventModel object正在使用正确的值创建。

However in my test when I call _pubClientMock.Verify();但是在我的测试中,当我调用_pubClientMock.Verify(); I get a System.NullReferenceException : It seems like the x.Cost is NULL here.我得到一个System.NullReferenceException :这里的x.Cost似乎是 NULL。

Any idea why this property would be NULL or what I'm doing wrong here?知道为什么这个属性会是 NULL 或者我在这里做错了什么吗?

So to iterate again, actually calling the .Sync() and stepping through the code with the debugger works perfectly.所以再次迭代,实际上调用.Sync()并使用调试器单步执行代码可以完美地工作。 It's the _pubClientMock.Verify that fails with with a NullReferenceException . _pubClientMock.VerifyNullReferenceException而失败。

Similar to the one of the already provided answers I would suggest relaxing the setup to avoid issues with referential equality by using It.IsAny .类似于已经提供的答案之一,我建议通过使用It.IsAny来放松设置以避免引用相等问题。 This will allow the test case to flow to completion.这将允许测试用例流向完成。

As for the null reference error in the Verification, because the updated example will have a situation where one of the event models will have a null Cost property value, you will need to check for null when verifying the predicate至于 Verification 中的 null 引用错误,由于更新后的示例会出现其中一个事件模型的Cost属性值为 null 的情况,因此在验证谓词时需要检查 null

[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")));
    }
}

Can you provide more detail about Log you pass through GetAndSendCost() maybe the problem comes from that part, you can use It.IsAny<EventModel>()您能否提供有关通过 GetAndSendCost() 传递的 Log 的更多详细信息,也许问题来自该部分,您可以使用 It.IsAny<EventModel>()

// 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")));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM