模拟复杂的对象 - 用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个选择:
- 你反对真正的Db =集成测试,这将确保你的FK约束,级联删除等正常工作。我建议创建一些
DemoDataDbInit
类,它将执行初始演示数据的真实Db插入。对于测试,你可以在第一次测试之前调用一次,保存“clean”初始化Db的快照(克隆),然后为后续测试你只需要attach this snapshot(假设你使用MSSql),这样你的测试就独立于每个其他(每次测试都有干净的Db)。您可以在CI过程中重复使用相同的DemoDataDbInit
类,以便在重新部署应用程序后获得一些初始虚拟数据。测试只是假设Db中有那些数据。 Docker也在这里考虑。可悲的是,我忘记了一个工具的名称 - 它是一个非常快速的内存Db,可用作nuget包,它与EF和nHib兼容,可用于测试 - 它可以提供一些加速(我相信它不是SQLite)在内存模式下)。 - 你反对模拟。我建议你创建一些
InMemoryDb
类,它将是一个工厂,内部使用Moq(或其他模拟手段)为你创建一个设置ISession
。session.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测试的主要内容,如果未能解决你的问题,请参考以下文章