2

序言轨,SRP,和单元测试

这是我第一次尝试建立与心中SRP一个应用程序,真的试图用测试来驱动代码的网站,而不是开始我数据架构(ActiveRecord),然后构建适合的应用程序。

但我遇到了问题。我已经订阅并观看了很多销毁所有软件的屏幕录像,理论上我喜欢他所讲的内容,但我在实践中遇到了麻烦。手头

问题,我知道我的应用程序的主要功能将搜索基于位置配置文件。所以我为此写了一个简单的Cucumber功能(故意暂时放弃路由/控制器/等等,以简化手头的任务)。

search.feature:

Feature: Search for profiles 

    Scenario: By zipcode 
    When I search for 90210 
    Then I will see profiles near 90210 

search_steps.rb:

When /^I search for 90210$/ do 
    @profiles = ProfileSearch.new.near_location(90210).all 
end 

Then /^I will see profiles near 90210$/ do 
    pending # express the regexp above with the code you wish you had 
end 

没有问题,现在到规格:

profile_search_spec.rb:

require_relative '../../app/services/profile_search' 

describe ProfileSearch do 
    it "finds nearby profiles" 
    it "does not find far away profiles" 
end 

profile_search .rb:

class ProfileSearch 
end 

我已经使用了一个ProfileSearch类的原因有几个。

  1. 在ActiveRecord(单一职责原则)之外尽可能多地移动业务逻辑似乎是正确的做法。
  2. PORO使得测试更快(无需加载Rails)。
  3. 我打算在不久的将来使用ElasticSearch或Solr,并希望界面保持不变。

我不确定接下来要做什么。 ProfileSearch显然取决于Profile模型,我很确定这将是ActiveRecord。

所以问题是我开始specing和建设Profile,并开始在我的测试中加载Rails?这似乎是最简单的选择,但有关它似乎是错误的。我觉得我会设计和构建我的应用尚未特别要求的行为。我不得不考虑领域,关系和存储等等,所有这些我的应用程序目前都不应该关心。

或者我应该在我的ProfileSearch规范中使用存根/嘲all对Profile的所有调用,并确保调用正确的方法?这也看起来是错误的,因为我不会真正测试行为,而且即使预期会出现相同的行为,切换到Solr或ElasticSearch时也必须重写测试。

或者我应该真正创造建立他的维基的事情时,不使用ActiveRecord暂且但正确响应所有正确的方法为Bob大叔表现出了工作档案模式?这似乎是理论上可能是最好的方法,但知道我将在未来使用ActiveRecord,它似乎也是相当多余的。

或者...他妈的它扔在ActiveRecord的模型中的方方面面:\

有这么多的模式,原则和最佳实践漂浮在我的头上,我不知道跆拳道做。

你会怎么做?

回答

1

首先,你需要选择你的测试方法:

  • 黄瓜是专为BDD(外而内)测试。黄瓜情景是为了锻炼你的整个堆栈(网页,控制器,模型,数据库 - 无论你的堆栈),而不是作为单元测试,你似乎做以上。所以,如果你写的第一件事是黄瓜的情况下,你需要将所有层的调入是进行第一次场景通过。无论是使用Cucumber(我最喜欢的)还是RSpec功能规格,我都会这样做。完成之后,您可以自由地重构以满足您喜欢的任何设计标准,并且可以使用单元测试来测试详细的需求。

  • 或者,你可以挑选堆栈中最棘手的部分,存在的单元测试,以及单元测试层和最终验收测试,直到你有一个完整的应用程序。一方面,在投资简单零件之前,证明您可以使棘手的零件工作,这可能是一个好主意。另一方面,我发现,当我想在组件接口没有工作的验收测试,我总是猜错的话,所以我不做这种方式。

无论哪种方式,你的目标是ProfileSearch类返回Profile实例,但不承诺他们是ActiveRecord对象或其他任何持久性特异性。你如何处理在测试这些类取决于测试的类型:

  • 如果使用验收规范(黄瓜或RSpec的功能规格),他们将不会使用存根,他们将利用一切真正的实现类,和他们会加载Rails,因为这是他们的要点,这就是他们的设计。对不起,他们会很慢。除了需要指定重要场景之外,不要再编写它们。

  • 在使用ProfileSearch类单元测试,您可以存根出去,速度返回波罗斯。 (如果您使用RSpec的护栏,require 'spec_helper'在这些规范不rails_helper避免加载Rails在自己运行它们。)

  • 做出的ProfileSearch本身通过单元测试,最简单的方法可能是只是让Profile ActiveRecord模型,并有ProfileSearch使用Profile的ActiveRecord的功能和返回Profile真实情况。这些规格也需要require 'rails_helper'

+0

完全支持的第一个点。我们称这些测试为“[皮下测试]”(http://martinfowler.com/bliki/SubcutaneousTest)。HTML)“,并且通常从控制器开始,并通过堆栈完全集成到真正的数据库(通常是嵌入式版本,因此它可以在内存中运行),就像Dave说的那样,它可以很好地重构控制器的许多部分模型和持久性问题可以重构,而且测试不需要改变,当有IO抽象时,这些问题通常会被嘲笑,因为它们并不重要,我在构建这些方法时采取了测试优先的方法。 – 2016-12-22 14:56:11