简体   繁体   English

如何隔离数据源,如DbSet?

[英]How can I isolate a data source such as DbSet?

I have a number of controllers that I am testing, each of which has a dependency on a repository. 我有许多我正在测试的控制器,每个控制器都依赖于存储库。 This is how I am supplying the mocked repository in the case of each test fixture: 这就是我在每个测试夹具的情况下提供模拟存储库的方式:

[SetUp]
public void SetUp()
{
    var repository = RepositoryMockHelper.MockRepository();
    controller = new HomeController(repository.Object);
}

And here is the MockRepository helper method for good measure: 这里是MockRepository帮助方法的好方法:

internal static Mock<IRepository> MockRepository()
{
    var repository = new Mock<IRepository>();

    var posts = new InMemoryDbSet<Post>()
    {
        new Post {
            ...
        },
        ...
    };

    repository.Setup(db => db.Posts).Returns(posts);

    return repository;
}
... = code removed for the sake of brevity. 

My intention is to use a new instance of InMemoryDbSet for each test. 我的目的是为每个测试使用一个新的InMemoryDbSet实例。 I thought that using the SetUp attribute would achieve this but clearly not. 我认为使用SetUp属性可以实现这一点,但显然不是。

When I run all of the tests, the results are inconsistent as the tests do not appear to be isolated. 当我运行所有测试时,结果不一致,因为测试似乎没有被隔离。 One test will for example, remove an element from from the data store and assert that the count has been decremented but according to the whim of the test runner, another test might have incremented the count, causing both tests to fail. 例如,一个测试将从数据存储中删除一个元素并断言计数已经减少但是根据测试运行器的异想天开,另一个测试可能会增加计数,导致两个测试都失败。

Am I approaching these tests in the correct way? 我是否以正确的方式接近这些测试? How can I resolve this issue? 我该如何解决这个问题?

The package you reference you are using for your InMemoryDataSet uses a static backing data structure, and so will persist across test runs. 您引用的用于InMemoryDataSet使用静态支持数据结构,因此将在测试运行期间保持不变 This is why you're seeing the inconsistent behaviors. 这就是你看到不一致行为的原因。 You can get around this by using another package (as you mention), or pass in a new HashSet to the constructor in every test so it doesn't use a static member. 您可以通过使用另一个包(如您所述)来解决这个问题,或者在每个测试中将新的HashSet传递给构造函数 ,这样它就不会使用静态成员。

As to the rest of your question, I think you're approaching the testing well. 至于你提出的其他问题,我认为你正在接近测试。 The only thing is that since all of your tests have the same setup (in your SetUp method), they could influence each other. 唯一的问题是,由于您的所有测试都具有相同的设置(在您的SetUp方法中),它们可能会相互影响。 Ie, if you have a test that relies on having no Foo objects in the set and one that needs at least one, then you're either going to add one in SetUp and remove it in the test that doesn't need it, or vice versa. 即,如果您的测试依赖于集合中没有Foo对象且需要至少一个Foo对象,那么您要么在SetUp添加一个并在不需要它的测试中删除它,或者反之亦然。 It could be more clear to have specific set up procedures as @BillSambrone mentioned. 具体的设置程序可以更清楚,如@BillSambrone所述。

As @PatrickQuirk pointed out, I think your problem is due to what InMemoryDbSet does under the covers. 正如@PatrickQuirk指出的那样,我认为你的问题是由于InMemoryDbSet在幕后做了什么。

Regarding the "Am I approaching this right ?" 关于“我接近这个权利吗?” part : 部分:

If, as I suspect, your Repository exposes some kind of IDbSet , it's probably a leaky abstraction. 如果我怀疑你的Repository暴露了某种IDbSet ,它可能是一个漏洞的抽象。 The contract of an IDbSet is far too specific for what a typical Repository client wants to do with the data. IDbSet的合同对于典型的Repository客户端想要对数据做什么来说太具体了。 Better to return an IEnumerable or some sort of read-only collection instead. 最好返回IEnumerable或某种只读集合。

From what you describe, it seems that consumers of Posts will manipulate it as a read-write collection. 根据您的描述, Posts消费者似乎会将其作为读写集合进行操作。 This isn't a typical Repository implementation - you'd normally have separate methods such as Get() , Add() , etc. The actual internal data collection is never exposed, which means you can easily stub or mock out just the individual Repository operations you need and not fear the kind of issues you had with your test data. 这不是典型的Repository实现 - 您通常有单独的方法,如Get()Add()等。实际的内部数据集合永远不会公开,这意味着您可以轻松地存根或模拟单个存储库您需要的操作,而不必担心您的测试数据存在的问题。

Whatever is in your [SetUp] method will be called for each test. 每次测试都会调用[SetUp]方法中的任何内容。 This is probably behavior that you don't want. 这可能是您不想要的行为。

You can either place the code you have in the [SetUp] method inside each individual test, or you can create a separate private method within your unit test class that will spin up a freshly mocked DbSet for you to keep things DRY. 你可以将你在[SetUp]方法中的代码放在每个单独的测试中,或者你可以在你的单元测试类中创建一个单独的私有方法,它将启动一个新模拟的DbSet,让你保持干燥。

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

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