简体   繁体   中英

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).

So to test some database method performing some actions on entity A, I need to save object of class A to database

//...
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.

My question is how to avoid such exception in elegant way, by using Moq. In my real problem the object trees are far more complex than in this simplified question.

You have 2 options:

  1. You go against the real Db = integration testing, which would ensure your FK constraints, cascade deletes etc. work correctly. I'd suggest creating some DemoDataDbInit class, which would perform real Db inserts of your initial demo data. 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). You could reuse the same DemoDataDbInit class in your CI process, to have some initial dummy data after the App is redeployed. Tests would just assume those data are there in the Db. Also Docker comes into consideration here. 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).

  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.

    • session.Save(entity) would add the entity to a list (mocked IRepository ) in background
    • 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.
    • If you'd feel like it, a builder, allowing you to write new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment") etc.

You would reuse this InMemoryDb in all your tests. 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 .

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. Either you go the full distance with the real DB, or you go in-memory with mocks - while pretending your FK relations are correct. Just for info: EF Core has a native in-memory mode .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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