FakeItEasy - 如何验证嵌套参数值 C#

FakeItEasy - How to verify nested arguments value C#

我需要您的帮助,以便找到一种方法来验证作为被测调用方法的参数传递的嵌套对象的值。 假设这个 class:

public class AuditTrailValueObject
{
    public ActionType Action { get; private set; }
    public EntityType EntityType { get; private set; }
    public long EntityId { get; private set; }
    public DateTime StartTime { get; private set; }
    public bool IsSuccess { get; private set; }
    public string Remarks { get; private set; }

    public AuditTrailValueObject(ActionType action, EntityType entityType, long entityId, DateTime startTime, bool isSuccess, string remarks = "")
    {
        Action = action;
        EntityType = entityType;
        EntityId = entityId;
        StartTime = startTime;
        IsSuccess = isSuccess;
        Remarks = remarks;
    }
}

并且以下接口将此 class 作为注入的依赖项:

public interface IAuditTrailService
{
    void WriteToAuditTrail(AuditTrailValueObject auditParamData);
}

现在我有 ScanService 取决于 AuditTrailService(实现 IAuditTrailService):

public long CreateScanRequest(long projectId)
{
    ScanRequestWriteModel scanRequest = _scanRequestWriteModelFactory.Create(projectDetails);

    long scanRequestId = _scanRequestsWriteRepository.Insert(scanRequest);

    _auditTrailService.WriteToAuditTrail(new AuditTrailValueObject(ActionType.Run, EntityType.SastScanRequest, scanRequestId, DateTime.UtcNow, true));

    return scanRequestId;
}

我写的测试:

[TestMethod]
public void Scan_GivenProjectId_ShouldAuditSuccess()
{
    //Given
    var projectId = 100;

    var scanService = CreateScanService();

    ...
    A.CallTo(() => _scanRequestWriteModelFactory.Create(projectDetails)).Returns(new ScanRequestWriteModel());
    A.CallTo(() => _scanRequestsWriteRepository.Insert(A<ScanRequestWriteModel>._)).Returns(1);

    //When
    var scanRequestId = scanService.CreateScanRequest(projectId);

    //Then
     A.CallTo(() => _auditTrailService.WriteToAuditTrail(
                        new AuditTrailValueObject(ActionType.Run, EntityType.SastScanRequest, scanRequestId, A<DateTime>._, true, A<string>._))).MustHaveHappened();
}

当 运行 这个测试我得到:

System.InvalidCastException: Specified cast is not valid

如何验证 AuditTrailValueObject 中嵌套参数的值?

您的问题是一个更大问题的征兆:您试图在一次测试中做太多事情。

因为您在 WriteToAuditTrail() 方法中新建了一个 AuditTrailValueObject 实例,您将无法访问该对象实例,因为它是在方法范围内创建的,因此不受检查。

但是,您首先希望访问该对象的唯一原因似乎是为了验证其中设置的值是否正确。

在这些值中,只有一个(就您的代码示例允许我们知道的而言)是从调用方法中设置的。这是来自对 _scanRequestsWriteRepository.Insert() 的调用的 return 值,它应该是它自己的单元测试的主题,您可以在其中独立于它的使用位置来验证正确的行为。

编写此单元测试(在 _scanRequestsWriteRepository.Insert() 方法上)实际上将解决问题的根本原因(您对单个测试做的太多)。但是,您眼前的问题仍然需要解决。执行此操作的最简单方法是完全删除 AuditTrailValueObject class,并将您的参数直接传递给对 WriteToAuditTrail() 的调用。

If I'll remove AuditTrailValueObject where the place should I verify what params are being passed to the auditTrailService? What I mean is that also if I've tested the auditTrailService I need to know that scan service call if with the right parameters (for example: with ActionType.Run and not with ActionType.Update).

要验证是否已将正确的参数传递给对 WriteToAuditTrail() 的调用,您可以注入伪造的 IAuditTrailService 并验证您的调用是否已发生:

A.CallTo(
    () => _auditTrailService.WriteToAuditTrail(
                    ActionType.Run, 
                    EntityType.SastScanRequest, 
                    scanRequestId, 
                    myDateTime, 
                    true, 
                    myString)
).MustHaveHappened();

@tom redfern 提出了很多要点,您可能需要解决这些问题。但是在重新阅读您的代码和评论之后,我认为我可以立即前进。您的代码至少有一个问题,也可能有另一个问题。

我们来看看

A.CallTo(() => _auditTrailService.WriteToAuditTrail(
                        new AuditTrailValueObject(ActionType.Run,
                                                  EntityType.SastScanRequest,
                                                  scanRequestId,
                                                  A<DateTime>._,
                                                  true
                                                  A<string>._)))
        .MustHaveHappened();

_ 结构在 AuditTrailValueObject 构造函数内部使用,它们在那里无效。它们会导致将默认值分配给 AuditTrailValueObject(DateTime.MinValue 和 null,我认为),并且几乎不是您想要的。如果将 new 提取到上一行,您会看到 FakeItEasy 在使用 _ 时抛出错误。我认为它应该能更好地帮助您找到代码中的问题,但我不确定这是否可能。我创建了 FakeItEasy Issue 1177 - 参数约束 That,当在 A.CallTo 中嵌套得更深时,会误报正在匹配的内容 以帮助 FakeItEasy 改进。

与此相关的是FakeItEasy如何匹配对象。当提供要比较的值时,(new AuditTrailValueObject(…) 的结果)FakeItEasy 将使用 Equals 将对象与接收到的参数进行比较。除非你的 AuditTrailValueObject 有一个好的 Equals,否则这将失败。

如果您想继续使用 AuditTrailValueObject 并且不想提供 Equals(那会忽略 startTime 和备注),还有其他方法。

首先,您可以使用 That.Matches,像这样:

A.CallTo(() => _auditTrailService.WriteToAuditTrail(A<AuditTrailValueObject>.That.Matches(
                                                        a => a.Action == ActionType.Run &&
                                                        a.EntityType == EntityType.SastScanRequest &&
                                                        a.EntityId == scanRequestId &&
                                                        a.IsSuccess)))
                                                    .MustHaveHappened();

有些人对 Matches 中的复杂约束并不狂热,因此另一种方法是捕获 AuditTrailValueObject 并在稍后询问它,正如 Alex James Brown 在他对 [ 的回答中所描述的那样=28=].