简体   繁体   English

测试:是否有存根模型方法?

[英]Testing: Stub model methods or not?

I do two kinds of tests: unit tests and integration tests (Cucumber/Capybara for the latter). 我进行两种测试:单元测试和集成测试(对于后者,则为Cucumber / Capybara)。

For unit testing, I wonder whether it's best practice to stub/mock methods of models when testing other classes? 对于单元测试,我想知道在测试其他类时是否存根/模拟模型方法是否是最佳实践?

# model
class Price < ActiveRecord::Base
  belongs_to :price_type, inverse_of: :prices
  belongs_to :book, inverse_of: :prices

  scope :for_book, ->(book) { where("book_id = ?", book) }
  scope :for_price_type, ->(price_type) { where("price_type_id = ?", price_type) }

  def self.latest_book_url(book, competitor)
    latest_price = for_book(book).for_competitor(competitor).last
    latest_price ? latest_price.book_url : nil
  end
end

If I test a class that calls these scopes or the class method latest_book_url , should I mock/stub scopes and methods of the model or use FactoryGirl to save records and then call the methods on the model in the test? 如果我测试了调用这些作用域的类或类方法latest_book_url ,则应该模拟/存根模型的作用域和方法,还是使用FactoryGirl保存记录,然后在测试中调用模型上的方法?

# class to be tested
class PriceLister
  def initialize(book)
    @book = book
    @competitors = book.competitors
    @price_types = PriceType.all
  end

  def list_book_urls_by_competitor
    price_list = {}
    @competitors.each do |competitor|
      price_list[competitor] = {}      
      price_list[competitor][:book_url] = Price.latest_book_url(@book, competitor)
      @price_types.each do |price_type|
        price_list[competitor][price_type] = Price.for_book(@book).for_competitor(competitor).for_price_type(price_type).last
      end                
    end
    price_list
  end
end

By definition Unit testing is the tesing of a single Unit of code. 按照定义,单元测试是单个代码单元的测试。 If you aren't mocking/stubbing things out, then are you really testing a single unit or are you running a small integration test? 如果您不是在模拟/测试,那么您是在测试单个单元还是在进行小型集成测试? The best practice is to Mock/Stub for your unit tests 最佳做法是模拟/存根进行单元测试

Assuming that you are still talking about unit testing for your second question, then Yes, you should now mock/stub out the code that you have already tested. 假设您仍然在谈论第二个问题的单元测试,那么是的,现在您应该模拟/存根已经测试过的代码。

The reasons for this are quite simple. 原因很简单。 If you change your code and break tests, you want to narrow down the code that caused the problem as easily as possible. 如果更改代码并破坏测试,则希望尽可能缩小导致问题的代码的范围。 If you have broken many tests because they all call the same piece of code, then suddenly you have many tests to check out. 如果您因为许多测试都调用相同的代码而破坏了许多测试,那么突然您将有许多测试要签出。 If you mocked out that call throughout all your other tests then only the tests that really test that unit will fail, not the ones that depend on it working correctly. 如果您在所有其他测试中都嘲笑了该调用,那么只有真正测试该单元的测试才会失败,而依赖于该单元的测试才能正常运行。

Think of it like an assumption. 认为它是一个假设。 If you assume your method is working, because your unit tests for that method are working, and base other work on that assumption, it will be all fine, until that assumption is proven wrong. 如果您假设您的方法有效,因为该方法的单元测试正在运行,并且其他工作都基于该假设,那么一切都很好,直到证明该假设是错误的为止。 At that point everything falls over. 那时一切都崩溃了。 If on the other hand you assume that the method will be wrong and instead replace it with something that is a known result (ie a mocked method givne a fixed result) then your other code is isolated from that problem. 另一方面,如果您认为该方法将是错误的,而是用已知结果(例如,模拟方法给出的固定结果)替换该方法,则您的其他代码将与该问题隔离。

In unit test, you can stub the methods from Price when you are testing PriceLister. 在单元测试中,可以在测试PriceLister时从Price中提取方法。 You should have separate unit tests for Price methods. 对于Price方法,应该有单独的单元测试。

And your integration tests should be testing the entire end-end functionality without stubbing anything 而且您的集成测试应该测试整个终端功能,而无需进行任何测试

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

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