使用 FakeItEasy 检查对多个返回的、包装的假货的调用

Checking calls on multiple returned, wrapped fakes with FakeItEasy

我有两个这样的界面:

public interface Store { Tx BeginTx() }
public interface Tx { void Write() }

我有这样的方法:

void WriteALot(fakeStore) 
{
    var tx1 = fakeStore.BeginTx();
    tx1.Write();

    var tx2 = fakeStore.BeginTx();
    tx2.Write();
}

还有一个测试:

var fakeStore = A.Fake<Store>(x => x.Wrapping(realstore));

// A.CallTo(() => fakeStore.BeginTx()).ReturnAWrappedTx()?

WriteALot(fakeStore);

// Check that a total of two calls were made to the Tx's

这可以做到吗?

编辑:

我应该澄清一下,实际上会有数百个事务和对每个事务的多个写入调用。 Store和Tx的实现很复杂。这是用于集成测试,我使用 FakeItEasy 检查不同设置下的批处理行为。虽然它可能与库的预期用例有点太远了:)

我想我想问的是我是否可以收集并最好合并伪造的交易,而无需手动进行。我可以想象像 ReturnLazily 这样的东西具有收集返回的假货的副作用,但这非常难以管理且难以阅读(而且我无法让断言部分工作)。

假设你打算在上面写 tx1.Writetx2.Write,你可以很容易地检查每个事务是否被调用一次,这可能比检查总共进行了两次调用更有用:

public void Test()
{
    var realStore = new RealStore();
    var fakeStore = A.Fake<Store>(x => x.Wrapping(realStore));

    var realTransaction1 = new RealTransaction();
    var realTransaction2 = new RealTransaction();

    var wrappedTransaction1 = A.Fake<Tx>(options => options.Wrapping(realTransaction1));
    var wrappedTransaction2 = A.Fake<Tx>(options => options.Wrapping(realTransaction2));
    A.CallTo(() => fakeStore.BeginTx())
        .Returns(wrappedTransaction1).Once().Then
        .Returns(wrappedTransaction2);

    WriteALot(fakeStore);

    A.CallTo(() => wrappedTransaction1.Write()).MustHaveHappenedOnceExactly();
    A.CallTo(() => wrappedTransaction2.Write()).MustHaveHappenedOnceExactly();
}

但是如果您真的想确保进行了两次调用而不检查每个事务是否负责 1 次写入,您可以

[Test]
public void LaxTest()
{
    int numberOfTransactionCalls = 0;

    var realStore = new RealStore();
    var fakeStore = A.Fake<Store>(x => x.Wrapping(realStore));

    var realTransaction1 = new RealTransaction();
    var realTransaction2 = new RealTransaction();

    var wrappedTransaction1 = A.Fake<Tx>(options => options.Wrapping(realTransaction1));
    var wrappedTransaction2 = A.Fake<Tx>(options => options.Wrapping(realTransaction2));

    A.CallTo(() => wrappedTransaction1.Write()).Invokes(() => ++numberOfTransactionCalls);
    A.CallTo(() => wrappedTransaction2.Write()).Invokes(() => ++numberOfTransactionCalls);

    A.CallTo(() => fakeStore.BeginTx())
        .Returns(wrappedTransaction1).Once().Then
        .Returns(wrappedTransaction2);

    WriteALot(fakeStore);

    Assert.That(numberOfTransactionCalls, Is.EqualTo(2));
}

请注意,如果您的生产方法真的像您一样简单 post,则无需委托给实际实现,您可以省略所有包装:

[Test]
public void UnwrappedTest()
{
    var fakeStore = A.Fake<Store>();
    var transaction1 = A.Fake<Tx>();
    var transaction2 = A.Fake<Tx>();
    A.CallTo(() => fakeStore.BeginTx())
        .Returns(transaction1).Once().Then
        .Returns(transaction2);

    WriteALot(fakeStore);

    A.CallTo(() => transaction1.Write()).MustHaveHappenedOnceExactly();
    A.CallTo(() => transaction2.Write()).MustHaveHappenedOnceExactly();
}

在我看来,理解正在发生的事情要容易得多。但也许你只是为了问问题而简化了。

随着需求的更新,我尝试了这种东西,通过了。我敢肯定它仍然过于简单化,但听起来您的测试会多种多样且怪异,而且我真的没有机会编写适合您特定用例的东西。然而,通过引入一个小工厂 class,我实现了一定程度的可读性(在我看来),并且能够收集创建的交易:

private class TransactionFactory
{
    private readonly IList<Tx> allTransactions = new List<Tx>();

    public IEnumerable<Tx> AllTransactions => allTransactions;

    public Tx Create()
    {
        var realTransaction = new RealTransaction();
        var fakeTransaction = A.Fake<Tx>(options =>
                    options.Wrapping(realTransaction));
        allTransactions.Add(fakeTransaction);
        return fakeTransaction;
    }
}


[Test]
public void UpdatedTests()
{
    var realStore = new RealStore();
    var fakeStore = A.Fake<Store>(x => x.Wrapping(realStore));

    var transactionFactory = new TransactionFactory();

    A.CallTo(() => fakeStore.BeginTx()).ReturnsLazily(transactionFactory.Create);

    WriteALot(fakeStore);

    Assert.That(transactionFactory.AllTransactions.SelectMany(Fake.GetCalls).Count(),
                Is.EqualTo(2));
}

这应该可以进行各种修改,但正如您所指出的,这并不是 FakeItEasy 预期的使用方式,因此您最终可能会围绕库进行大量自定义编码。