使用FakeItEasy

时间:2018-10-09 12:37:42

标签: 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

可以做到吗?

编辑:

我应该澄清,实际上将有数百个事务,并且每个事务都有多个Write-call。并且Store和Tx的实现非常复杂。这是一个集成测试,我使用FakeItEasy来检查不同设置下的批处理行为。不过,这可能与该库的预期用例相差太远了:)

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

2 个答案:

答案 0 :(得分:1)

假设您打算在上面编写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));
}

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

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

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

答案 1 :(得分:1)

在更新的要求下,我尝试了这种方法,并通过了。我敢肯定它仍然过于简单化,但是听起来您的测试会变得千变万化,而且我确实没有机会编写适合您特定用例的东西。但是,通过引入一个小的工厂类,我达到了某种程度的可读性(在我看来),并且能够收集所创建的事务:

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的预期用途,因此您可能最终会在库周围进行大量自定义编码。