简体   繁体   English

集成测试:我做得对吗?

[英]Integration Testing: Am I doing it right?

Here's an integration test I wrote for a class that interacts with a database: 这是我为一个与数据库交互的类编写的集成测试:

[Test]
public void SaveUser()
{
    // Arrange
    var user = new User();
    // Set a bunch of properties of the above User object

    // Act
    var usersCountPreSave = repository.SearchSubscribersByUsername(user.Username).Count();
    repository.Save(user);
    var usersCountPostSave = repository.SearchSubscribersByUsername(user.Username).Count();

    // Assert
    Assert.AreEqual(userCountPreSave + 1, userCountPostSave);
}

It seems to me that I can't test the Save function without involving the SearchSubscriberByUsername function to find out if the user was successfully saved. 在我看来,我不能在不涉及SearchSubscriberByUsername函数的情况下测试Save函数,以确定用户是否已成功保存。 I realize that integration tests aren't meant to be unit tests which are supposed to test one unit of code at a time. 我意识到集成测试并不是单元测试,它应该一次测试一个代码单元。 But ideally, it would be nice if I could test one function in my repository class per test but I don't know how I can accomplish that. 但理想情况下,如果我可以在每个测试中测试我的存储库类中的一个函数,那将会很好,但我不知道如何实现这一点。

Is it fine how I've written the code so far or is there a better way? 我到目前为止编写代码还是有更好的方法可以吗?

You have a problem with your test. 您的测试有问题。 When you're testing that data is saved into the database, you should be testing that it's in the database, not that the repository says that it's in the database. 当您测试将数据保存到数据库中时,您应该测试它是否在数据库中,而不是存储库说它在数据库中。

If you're testing the functionality of repository, then you can't verify that functionality by asking if it has done it correctly. 如果您正在测试存储库的功能,那么您无法通过询问它是否已正确完成来验证该功能。 It's the equivalent of saying to someone 'Did you do this correctly?' 这相当于对某人说'你做得对吗?' They are going to say yes. 他们会说是的。

Imagine that repository never commits. 想象一下,存储库永远不会提交。 Your test will pass fine, but the data won't be in the database. 您的测试将正常通过,但数据将不在数据库中。

So, what I would do is to to open a connection (pure SQL) to the database and check that the data has been saved correctly. 那么,我要做的是打开一个连接(纯SQL)到数据库,并检查数据是否已正确保存。 You only need to a select count(*) before and after to ensure that the user has been saved. 您只需要在之前和之后选择计数(*)以确保用户已被保存。 If you do this, you can avoid using the SearchSubscribersByUsername as well. 如果这样做,您也可以避免使用SearchSubscribersByUsername。

If you're testing the functionality of repository, you can't trust repository, by definition. 如果您正在测试存储库的功能,则根据定义,您无法信任存储库。

To unit test something like a "Save" function, you will definitely need some trustworthy channel to check the result of the operation. 要对像“保存”功能这样的单元测试进行单元测试,您肯定需要一些值得信赖的通道来检查操作的结果。 If you trust SearchSubscribersByUsername (because you have already made some unit tests for that function on its own), you can use it here. 如果您信任SearchSubscribersByUsername (因为您已经为该功能SearchSubscribersByUsername进行了一些单元测试),您可以在此处使用它。

If you don't trust SearchSubscribersByUsername and you think your unit test could also break because there is an error in that function (and not in Save ), you should think about a different channel (perhaps you have a possibility to make a bypassing SQL access to your DB to check the Save result, which may be simpler than the implementation of SearchSubscribersByUsername )? 如果您不信任SearchSubscribersByUsername并且您认为您的单元测试也可能因为该函数中存在错误(而不是Save )而中断,您应该考虑使用不同的通道(也许您可以绕过SQL访问)到您的数据库检查Save结果,这可能比SearchSubscribersByUsername的实现更简单? However, do not reimplement SearchSubscribersByUsername again, that would be getting pointless. 但是,不要再次重新实现SearchSubscribersByUsername ,这将变得毫无意义。 Either way, you will need at least some other function you can trust. 无论哪种方式,您至少需要一些您可以信赖的其他功能。

Unless the method you are testing returns definitive information about what you have done I don't see any way to avoid calling other methods. 除非您测试的方法返回有关您所做的事情的确切信息,否则我认为没有办法避免调用其他方法。 I think you are correct in your assumption that Integration testing needs a different way of thinking from Unit testing. 我认为你的假设是正确的,因为集成测试需要一种不同的单元测试思维方式​​。

I would still build tests that focus on individual methods. 我仍然会构建专注于个别方法的测试。 So in testing Save() I may well use the capabilities of Search(), but my focus is on the edge cases of Save(). 所以在测试Save()时我可能会使用Search()的功能,但我的重点是Save()的边缘情况。 I build tests that deal with duplicate insertions or invalid input data. 我构建了处理重复插入或无效输入数据的测试。 Then later I build a whole raft of Search() tests that deal with the edge cases of Search(). 然后我构建了一大堆Search()测试来处理Search()的边缘情况。

Now one possible way of thinking is that Save and Search have some commonality, a bug in Search might mask a bug in Save. 现在一种可能的思维方式是Save和Search有一些共性,Search中的错误可能会掩盖Save中的错误。 Imagine, for example, if you had a caching layer down there. 想象一下,例如,如果你有一个缓存层。 So possibly an alternative approach is to use some other verification mechanism. 因此,另一种方法可能是使用其他一些验证机制。 For example a direct JDBC call to the database, or alteratively introducing mocking layers at some point in your infrastructure. 例如,对数据库的直接JDBC调用,或者在基础结构中的某个位置替代地引入模拟层。 When building complex Integrated Systems this kind of "Back Door" verification may be essential. 在构建复杂的集成系统时,这种“后门”验证可能是必不可少的。

Personally, I've written countless tests very similar to this and think its fine. 就个人而言,我已经写了无数与此非常相似的测试并认为它很好。 The alternative is to stub out the database so that searchSubscribers never actually does anything but thats great deal of work for what I'd say is little gain. 另一种方法是将数据库存根,以便searchSubscribers永远不会做任何事情,但我所说的大量工作都没有什么好处。

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

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