简体   繁体   English

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

[英]Mocking complex object - testing with Moq

I have following Object structure: 我有以下对象结构:

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

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

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

I want to create some tests to written database access code. 我想创建一些测试来编写数据库访问代码。 To achieve this, before each test, I will add do database some neccessary data (I am using fluent nHibernate). 为此,在每次测试之前,我都会向数据库添加一些必要的数据(我使用的是流畅的nHibernate)。

So to test some database method performing some actions on entity A, I need to save object of class A to database 因此,为了测试对实体A执行某些操作的某些数据库方法,我需要将类A的对象保存到数据库中

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

Which leads to nullreference exception, because objects of classes B and C are also database entities that can't be nullable. 这会导致nullreference异常,因为类B和C的对象也是不能为null的数据库实体。

My question is how to avoid such exception in elegant way, by using Moq. 我的问题是如何使用Moq以优雅的方式避免此类异常。 In my real problem the object trees are far more complex than in this simplified question. 在我真正的问题中,对象树比这个简化的问题要复杂得多。

You have 2 options: 您有2个选择:

  1. You go against the real Db = integration testing, which would ensure your FK constraints, cascade deletes etc. work correctly. 您违反了真正的Db =集成测试,这将确保您的FK约束,级联删除等工作正常。 I'd suggest creating some DemoDataDbInit class, which would perform real Db inserts of your initial demo data. 我建议创建一些DemoDataDbInit类,该类将对初始演示数据进行真正的Db插入。 For tests, you would call this once before the first test, save a snapshot (clone) of the "clean" initialized Db, then for subsequent tests you would just attach this snapshot (assuming you use MSSql), so that your tests are independent of each other (you have clean Db for each test). 对于测试,您将在第一次测试之前调用一次,保存“干净”初始化的Db的快照(克隆),然后对于后续测试,只需附加此快照 (假设您使用MSSql),这样您的测试就可以独立进行了。彼此(每个测试都有干净的Db)。 You could reuse the same DemoDataDbInit class in your CI process, to have some initial dummy data after the App is redeployed. 您可以在CI流程中重用相同的DemoDataDbInit类,以在重新部署App后获得一些初始虚拟数据。 Tests would just assume those data are there in the Db. 测试只是假设那些数据在Db中。 Also Docker comes into consideration here. Docker也在这里考虑。 Sadly, I forgot the name of one tool - it was a very fast in-memory Db available as nuget package, which was compatible with EF & nHib, usable for tests - it would provide some speed-up (I believe it was not SQLite in in-memory mode). 遗憾的是,我忘记了一种工具的名称-它是一种非常快的内存中Db,可作为nuget软件包使用,它与EF&nHib兼容,可用于测试-它会提供一定的提速(我相信这不是SQLite在内存模式下)。

  2. You go against a mock. 你反对一个模拟。 I'd suggest you create some InMemoryDb class, which would be a factory, internally using Moq (or other mocking means) to create a set up ISession for you. 我建议您创建一些InMemoryDb类,这将是一个工厂,在内部使用Moq(或其他模拟方法)为您创建一个设置的ISession

    • session.Save(entity) would add the entity to a list (mocked IRepository ) in background session.Save(entity)会将session.Save(entity)添加到后台列表(模拟的IRepository )中
    • It would have some preset groups with some often useful standard data 它将具有一些预设组以及一些经常有用的标准数据

    vas session = new InMemoryDb.TwoCustomersWithThreeOrdersEach();

    • It would expose all entities as instance properties with fitting name such as Customer1Order1 so that you could easily access them for additional arrange or assert. 它将所有实体公开为具有合适名称(例如Customer1Order1实例属性,以便您可以轻松访问它们以进行其他安排或声明。
    • If you'd feel like it, a builder, allowing you to write new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment") etc. 如果您愿意,可以使用一个生成器,允许您编写new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment")等。

You would reuse this InMemoryDb in all your tests. 您将在所有测试中重用此InMemoryDb You would need to manually set those bidirectional foreign key relations/navigation properties by hand - so that your queries wouldn't crash - as a part of the implementation of the InMemoryDb . 作为InMemoryDb实现的一部分,您需要手动设置那些双向外键关系/导航属性-这样您的查询就不会崩溃。

In my current project we use both approaches for integration & unit testing respectively, with success. 在我当前的项目中,我们分别使用两种方法进行集成和单元测试,均取得了成功。

Am not sure this is what you were hoping for, but I'm afraid there is no easy way to "mock yourself out" of the constraints the "real" nHibernate imposes. 不确定这是否是您的期望,但恐怕没有简单的方法可以“模拟” nHibernate施加的“约束”。 Either you go the full distance with the real DB, or you go in-memory with mocks - while pretending your FK relations are correct. 要么与真实的DB进行全力以赴,要么在模拟中进入内存-假装FK关系是正确的。 Just for info: EF Core has a native in-memory mode . 仅供参考:EF Core具有本机内存模式

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM