简体   繁体   中英

NoSQL - How to mock database for unit testing?

I'm researching NoSQL databases and have a question regarding unit testing. What is the appropriate method to unit test the business logic? How does one mock a NoSQL database?

Your business logic should not touch the database directly, but rather go through a database access layer. This lets you mock that intermediate layer for unit testing. To do this, you can use dependency injection and mocking. There are frameworks that can help you with both of these things, but you can also do it by hand. Here's an example:

Say we have a DAL:

public class DBDataProvider: IDataProvider
{
    public string getData()
    {
         //SQL to get data from actual database.
    }
}

As you can see, this implements an interface for something that provides data for your business layer. It might look something like this:

public Interface IDataProvider
{
     String getData();
}

Your business layer might look something like this:

public BusinessClass
{
    private IDataProvider dataProvider;

    public BusinessClass()
    {
        dataProvider = new DBDataProvider();
    }

    public BusinessClass(IDataProvider provider)
    {
        dataProvider = provider;
    }

    public void doBusinessStuff()
    {
        dataProvider.getData(); 
        //Do something with data.
    }

}

So now in your production code, you will make your business class using the default constructor, which will automatically make your class with a connection to the DB. However, notice that we can create a BusinessClass with an IDataProvider that we specify. So, you can make a "fake" data provider just for testing:

public class MockDataProvider: IDataProvider
{
    public string getData()
    {
         //return some expected result that you can control, without doing a DB call.
    }
}

Now in your test, you can create a new MockDataProvider, and pass that into the constructor for BusinessClass. Your business class will now use your mock data provider, instead of the real DB.

Here I did everything by hand, but it gives you an idea of how this works. In real life, you can use mocking and dependency injection frameworks to write a bunch of that code for you.

The same way you mock any dependency. Write a nice, neat contract from which implementation details can be abstracted away, then mock that contract. Typically this is done by using the Data Access Layer as the contract(s).
Without getting into real implementation details, lets say you have a query in your method you want to test: (note, i copied this code from a ravenDB example, but i know 0 about ravenDB, so it might not even compile)

public void SomeMethod()
{
    var name = "Hello";
    var motto = "World";                       
    using (var docStore = new DocumentStore("localhost", 8080).Initialize())
    using (var session = documentStore.OpenSession()){
        session.Store(new Company { Name = name, Motto = motto });;
        session.SaveChanges();
    }
}

That's going to be pretty hard to mock / test because it requires a db on localhost on 8080. Now, if you separate this logic out into another class:

public class AwesomeDAL
    public virtual void AddCompany(string name, string motto){
        using (var docStore = new DocumentStore("localhost", 8080).Initialize())
        using (var session = documentStore.OpenSession()){
            session.Store(new Company { Name = name, Motto = motto });;
            session.SaveChanges();
        }
}

and allow for the injection of the dependency (AwesomeDal):

public class ClassBeingTested
{
    public AwesomeDal DAL { get; set; }
    public ClassBeingTested() : this(new AwesomeDal()){}
    public ClassBeingTested(AwesomeDal dal)
    {
       this.DAL = dal;
    }

    public void SomeMethod()
    {
        var name = "Hello";
        var motto = "World";                       
        this.DAL.AddCompany(name, motto);
    }
}

And you can now test the BL code in isolation. Or you can simulate database exceptions, or anything else you need to test because your data access layer is abstracted, and its implementation is easily mockable with a framework like Moq or RhinoMocks

In addition to the already posted (correct) answers, let me propose an alternative solution: Use a real development database. Nothing is a more realistic mock that the real thing, If you test on it directly. at least you know that your code will actually run.

If you can abstract away your database easily, I recommend doing that, though.

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