模拟复杂的对象 - 用Moq测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟复杂的对象 - 用Moq测试相关的知识,希望对你有一定的参考价值。

我有以下对象结构:

public class A
{
    //...
    public B b;
    //...
}

public class B
{
    //...
    public C c;
    //...
}

public class C
{
    //...
    //...
}

我想为编写的数据库访问代码创建一些测试。为了实现这一点,在每次测试之前,我将为数据库添加一些必要的数据(我使用流畅的nHibernate)。

因此,为了测试一些在实体A上执行某些操作的数据库方法,我需要将类A的对象保存到数据库中

//...
var a = new A();
session.Save(a);
//...

这导致nullreference异常,因为类B和C的对象也是不可为空的数据库实体。

我的问题是如何通过使用Moq以优雅的方式避免这种异常。在我真正的问题中,对象树比这个简化的问题复杂得多。

答案

你有2个选择:

  1. 你反对真正的Db =集成测试,这将确保你的FK约束,级联删除等正常工作。我建议创建一些DemoDataDbInit类,它将执行初始演示数据的真实Db插入。对于测试,你可以在第一次测试之前调用一次,保存“clean”初始化Db的快照(克隆),然后为后续测试你只需要attach this snapshot(假设你使用MSSql),这样你的测试就独立于每个其他(每次测试都有干净的Db)。您可以在CI过程中重复使用相同的DemoDataDbInit类,以便在重新部署应用程序后获得一些初始虚拟数据。测试只是假设Db中有那些数据。 Docker也在这里考虑。可悲的是,我忘记了一个工具的名称 - 它是一个非常快速的内存Db,可用作nuget包,它与EF和nHib兼容,可用于测试 - 它可以提供一些加速(我相信它不是SQLite)在内存模式下)。
  2. 你反对模拟。我建议你创建一些InMemoryDb类,它将是一个工厂,内部使用Moq(或其他模拟手段)为你创建一个设置ISessionsession.Save(entity)会将实体添加到背景列表(模拟IRepository)中 它会有一些预设组,其中包含一些常用的标准数据 vas session = new InMemoryDb.TwoCustomersWithThreeOrdersEach(); 它会将所有实体公开为具有拟合名称的实例属性,例如Customer1Order1,以便您可以轻松访问它们以进行其他排列或断言。 如果你有这样的感觉,那就是建筑师,允许你写new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment")等。

您将在所有测试中重复使用此InMemoryDb。您需要手动设置这些双向外键关系/导航属性 - 以便您的查询不会崩溃 - 作为InMemoryDb实现的一部分。

在我目前的项目中,我们分别使用两种方法进行集成和单元测试,并取得了成功。

我不确定这是你所希望的,但我担心没有简单的方法来“嘲笑”“真正的”nHibernate强加的约束。你可以与真正的数据库一起走完整个距离,或者你带着嘲讽进入内存 - 而假装你的FK关系是正确的。仅供参考:EF Core有本地in-memory mode

以上是关于模拟复杂的对象 - 用Moq测试的主要内容,如果未能解决你的问题,请参考以下文章

如何使用moq生成假数据进行单元测试?

单元测试之Mock(Moq)

使用 Moq 模拟单元测试的异步方法

如何使用 MOQ 框架在 c# 中模拟静态方法?

使用Moq模拟实体框架6 ObjectResult

使用 Moq 模拟 NHibernate ISession