模拟复杂对象-使用Moq进行测试

时间:2018-09-04 08:59:29

标签: c# nhibernate 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);
//...

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

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

1 个答案:

答案 0 :(得分:0)

您有2个选择:

  1. 您违反了真正的Db =集成测试,这将确保您的FK约束,级联删除等工作正常。我建议创建一些DemoDataDbInit类,该类将对初始演示数据进行真正的Db插入。对于测试,您将在第一次测试之前调用一次,保存“干净的”初始化Db的快照(克隆),然后对于后续测试,只需attach this snapshot(假设您使用MSSql),以便进行测试彼此独立(每个测试都有干净的Db)。您可以在CI流程中重用相同的DemoDataDbInit类,以在重新部署应用程序后获得一些初始虚拟数据。测试只是假设那些数据在Db中。 Docker也在这里考虑。 遗憾的是,我忘记了一种工具的名称-它是一种非常快的内存中Db,可作为nuget软件包使用,它与EF&nHib兼容,可用于测试-它会提供一定的提速(我相信这不是SQLite在内存模式下)。

  2. 您反对模拟。我建议您创建一个InMemoryDb类,这将是一个工厂,在内部使用Moq(或其他模拟方法)为您创建一个设置ISession

    • session.Save(entity)会将实体添加到后台列表(模拟IRepository)中
    • 它将有一些预设组,其中包含一些经常有用的标准数据

    vas session = new InMemoryDb.TwoCustomersWithThreeOrdersEach();

    • 它将所有实体公开为具有合适名称(例如Customer1Order1)的实例属性,以便您可以轻松访问它们以进行其他安排或声明。
    • 如果愿意,可以使用构建器,允许您编写new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment")等。

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

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

不确定这是否是您的期望,但恐怕没有简单的方法可以“摆脱” nHibernate施加的“约束”。要么与真实的DB进行全力以赴,要么在模拟中进入内存-假装FK关系是正确的。仅供参考:EF Core具有本机in-memory mode