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

时间:2017-07-31 08:44:43

标签: unit-testing fakeiteasy

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

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

以下接口将此类作为注入依赖项:

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:指定的强制转换无效

如何在AuditTrailValueObject

中验证嵌套参数的值

2 个答案:

答案 0 :(得分:2)

您的问题是一个更大问题的症状:您试图通过一次测试做太多。

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

但是,您首先想要访问此对象的唯一原因是,您可以验证其中设置的值是否正确。

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

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

  

如果我将删除AuditTrailValueObject,我应该验证该地点   什么参数传递给auditTrailService?我的意思是   如果我已经测试了auditTrailService,我也需要知道   如果使用正确的参数,则扫描服务调用(例如:with   ActionType.Run而不是ActionType.Update)。

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

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

答案 1 :(得分:2)

@tom redfern提出了许多好处,您可能想要解决这些问题。但在重新阅读您的代码和评论后,我认为我是一个直接的前进方向。您的代码至少有一个问题,可能还有另一个问题。

让我们来看看

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

_构造函数在AuditTrailValueObject构造函数中使用DateTime.MinValue构造,它们在那里无效。它们会导致将默认值分配给AuditTrailValueObject,(new和null,我认为),并且几乎不是您想要的。如果您将_提取到上一行,则在使用new AuditTrailValueObject(…)时,您会看到FakeItEasy抛出错误。我认为应该更好地帮助您在代码中找到问题,但我不确定它是否可行。我创建了FakeItEasy Issue 1177 - Argument constraint That, when nested deeper in A.CallTo, misreports what's being matched来帮助FakeItEasy改进。

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

如果您想继续使用Equals并且不想提供That.Matches(这会忽略startTime和备注),那么就有前进的方法。

首先,您可以使用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中的复杂约束并不感兴趣,因此另一种方法是捕获Me并稍后查询它,正如Alex James Brown在他对{的回答中所描述的那样{3}}