【问题标题】:FakeItEasy: Simple Fake not being usedFakeItEasy:没有使用简单的 Fake
【发布时间】:2018-11-09 12:35:10
【问题描述】:

我正在尝试使用 FakeItEasy(4.9.1 版)为一些遗留代码创建单元测试。我创建了两个单元测试,其中一个按预期工作,另一个非常相似,尽管经过大量试验和错误,但仍无法工作。

这是似乎无法测试的方法的摘录:

public class PosMessageProcessor : IPosMessageProcessor
{
    public void Execute()
    {
        PurchaseOrderRepository repo = new PurchaseOrderRepository();
        ...
        try
        {
            m_PurchaseOrder = GetOrderForProcessing(repo);
            ...

这是我的测试:

[TestMethod]
[TestCategory("POS")]
public void ExecuteTest()
{
    // Arrange
    PurchaseOrder purchaseOrder = GetPurchaseOrder();
    IPosMessageProcessor fakePosMessageProcessor = A.Fake<IPosMessageProcessor>();
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).Returns(purchaseOrder);

    // Act
    _posMessageProcessor.Execute();

    // Assert
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).MustHaveHappened();
}

_posMessageProcessor 变量是PosMessageProcessor 类的一个实例。我想捕捉对GetOrderForProcessing() 方法的调用(在Execute() 方法内)并让它返回我硬编码的purchaseOrder 对象。但是我得到了另一个返回值(null)。为什么?

有效的单元测试测试GetOrderForProcessing() 方法:

[TestMethod]
[TestCategory("POS")]
public void GetOrderForProcessingTest()
{
    // Arrange
    PurchaseOrder purchaseOrder = GetPurchaseOrder();
    IPurchaseOrderRepository fakePurchaseOrderRepository = A.Fake<IPurchaseOrderRepository>();
    A.CallTo(() => fakePurchaseOrderRepository.GetPurchaseOrderByOrderTrackingNumber(A<string>.Ignored)).Returns(purchaseOrder);

    // Act
    PurchaseOrder result = _posMessageProcessor.GetOrderForProcessing(fakePurchaseOrderRepository);

    // Assert
    A.CallTo(() => fakePurchaseOrderRepository.GetPurchaseOrderByOrderTrackingNumber(A<string>.Ignored)).MustHaveHappened();
    Assert.IsNotNull(result);
}

在这种情况下,对GetPurchaseOrderByOrderTrackingNumber() 的调用会按预期返回我的硬编码对象。这两个测试几乎相同,只是GetOrderForProcessing() 方法带有一个参数,而Execute() 没有。

【问题讨论】:

    标签: c# fakeiteasy


    【解决方案1】:

    ExecuteTest 失败是因为您配置了一个假的IPosMessageProcessor,然后在一个真正的PosMessageProcessor_posMessageProcessor 上调用Execute_posMessageProcessor,作为实际的PosMessageProcessor,执行其常规代码路径,调用实际的Execute,这将调用实际的GetOrderForProcessing

    (请注意,您实际上可以从测试中删除 fakePosMessageProcessor 变量以及对它的所有引用,并且行为将是相同的。)

    我不推荐这种测试,但为了伪造GetOrderForProcessing 并仍然测试实际的Execute 代码,您必须编写类似的测试

    public void NewExecuteTest()
    {
        // Arrange
        PurchaseOrder purchaseOrder = GetPurchaseOrder();
    
        // Get a fake PosMessageProcessor that falls back to original behaviour for all calls.
        IPosMessageProcessor fakePosMessageProcessor = A.Fake<PosMessageProcessor>(options => options.CallsBaseMethods());
    
        // Now fake out the GetOrderForProcessing call so we can test Execute.
        A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).Returns(purchaseOrder);
    
        // Act
        fakePosMessageProcessor.Execute();
    
        // Assert
        A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).MustHaveHappened();
    }
    

    【讨论】:

    • 非常感谢您的帮助。但是,我在安排部分的A.CallTo() 上收到FakeItEasy.Configuration.FakeConfigurationException 错误,说:无法拦截非虚拟成员。只能拦截接口成员和虚拟、覆盖和抽象成员。每当我将类(不是接口)放入A.Fake&lt;T&gt;() 时,都会发生这种情况。能够伪造课程是我离开 Rhino 的重要原因。我错过了其他选择吗?
    • 那是个问题。仅限虚拟会员can be overridden。这是基于 DynamicProxy 的框架的标准限制,例如 FakeItEasy、Moq 和 NSubstitute。我会看看您是否可以使该方法虚拟化或以其他方式更改您的协作模型。据我所知,如果您绝对需要配置非虚拟成员的能力,则需要使用 TypeMock Isolator 等商业框架。
    • 再次感谢。我从你的回答中了解到。最后,我能够截获对IPurchaseOrderRepository 上的方法的调用,这使它更直接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多