简体   繁体   English

NoSQL - 如何模拟数据库进行单元测试?

[英]NoSQL - How to mock database for unit testing?

I'm researching NoSQL databases and have a question regarding unit testing.我正在研究 NoSQL 数据库并且有一个关于单元测试的问题。 What is the appropriate method to unit test the business logic?对业务逻辑进行单元测试的合适方法是什么? How does one mock a NoSQL database?如何模拟 NoSQL 数据库?

Your business logic should not touch the database directly, but rather go through a database access layer.您的业务逻辑不应该直接接触数据库,而是通过数据库访问层 go。 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.为此,您可以使用依赖注入和 mocking。有些框架可以帮助您完成这两件事,但您也可以手动完成。 Here's an example:这是一个例子:

Say we have a DAL:假设我们有一个 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.所以现在在您的生产代码中,您将使用默认构造函数创建您的业务 class,这将自动使您的 class 连接到数据库。 However, notice that we can create a BusinessClass with an IDataProvider that we specify.但是,请注意,我们可以使用指定的 IDataProvider 创建 BusinessClass。 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.现在在您的测试中,您可以创建一个新的 MockDataProvider,并将其传递到 BusinessClass 的构造函数中。 Your business class will now use your mock data provider, instead of the real DB.您的业务 class 现在将使用您的模拟数据提供程序,而不是真正的数据库。

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.在现实生活中,您可以使用 mocking 和依赖注入框架为您编写一堆代码。

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)在不深入了解实际实现细节的情况下,假设您在要测试的方法中有一个查询:(注意,我从 ravenDB 示例中复制了这段代码,但我对 ravenDB 的了解为 0,因此它甚至可能无法编译)

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:这将很难模拟/测试,因为它需要 8080 本地主机上的数据库。现在,如果您将此逻辑分离到另一个 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):并允许注入依赖项 (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.您现在可以单独测试 BL 代码。 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或者你可以模拟数据库异常,或者任何你需要测试的东西,因为你的数据访问层是抽象的,它的实现很容易用Moq或 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.如果您可以轻松地抽象出您的数据库,我建议您这样做。

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

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