[Rails.Test.Prescriptions]系列之一:0基础的Test & Rails...

Before Start

个人简介

大学本科,大四,软件工程专业,新手。

目的

记录自己的读书,加强记忆。

注意

虽然学过测试,但基本忘干净了,虽然接触了一阵ruby on rails,但还是菜鸟等级,所以错误难免(还加入了不少个人理解),所以如果有读者,请自己分别对错。有错误欢迎前辈指出。另外因为本人也是菜鸟,所以基本上都会从Hello World开始。

Intro

哪怕一个刚刚开始学习软件的人,也知道软件测试必不可少,但是还是有无数的新手跳过这一部分(包括我自己),在撞碎了无数南墙之后,回过头来看测试。

为什么需要测试

  1. 节约时间。启动java服务器,哪怕是rails s的时间,也没有运行一个文件快。
  2. 节约人工测试成本。曾经无数次的打开浏览器一个页面一个页面的测试,一个表单一个表单的填写啊。
  3. 避免连锁反映。改了A,打开浏览器 , A页面OK了,结果因为影响到了B,有没有注意,导致B无法访问了,人工测试总有不全面的地方(当然测试也不一定总全面,但至少比人工强多了。)
  4. 保证代码质量(健壮性可扩展性等等等等,可以开始背名词了,下一个小节会更详细的介绍)

测试流程

无论是TDD还是BDD,都有一个Red-Green循环。写测试,失败–>写代码–>测试,成功(因为大多数测试失败是红色的 成功是绿色的 所以叫Red-Green循环,不过rails都是黑的。。。)

书里内容,详细的介绍了Red-Green循环

  1. 写测试,看着它失败。
  2. 写最简单的可以通过的代码,这个时候你不用考虑优雅简洁设计等问题。
  3. 测试,成功。
  4. 重构,我个人认为这一步很重要,重构之后继续让测试通过。接上一小节第四点,这样保证你的代码总是最精简可用的,少重复,没有破窗留在身后(破窗理论参见程序员的修炼之道)。

这里还有一个例子,参见最后那个完整的例子。

Rails中的测试

不截图了,以一个新手新建五分钟的项目来说,在项目中有个test文件夹,在test文件夹下面还有5个文件夹。

  • units 单元测试,恩,在rails里叫单元测试不太准确,准确的说是测试model层的。
  • functional 功能测试,ok,controller和views
  • fixtures 数据,测试的数据(等到model测试的时候会介绍用factory方式来生成测试数据)。
  • integrations 集成测试,简单的说就是 functionals一般测试一个controller的一个方法,集成测试可以测试跨controller的多个方法的连锁功能。
  • performances 性能测试,这个没有解释的必要了吧。

基本知识

units里面的tests一般是继承自ActiveSupport::TestCase( functionals里面的tests继承自ActionController::TestCase(<ActiveSupport::TestCase)

一个测试是指


test "should be unique" do
    #some assertions
end

在test后面的参数是如果测试失败输出的话,在代码块里应该放置你的操作和你预期的结果(无数assert方法)

另外每个TestCase还有setup和teardown方法,前者用于在所有test执行之前执行(假设一个类称为testcase 一个单独的test方法语句称为test),后者用于在所有test执行之后执行。
比如我在一个testcase里有test “should be a” do end 和test “should be b” do end 两个测试,执行顺序是setup test_a teardown 然后再setup test_b teardown

比如你每个test都要使用一个user对象 就可以在setup中构建这个user
@user = User.create(:name=>“John”)

ActiveSupport 对 原本的testcase进行了扩展,可以在一个测试内放置多个setup。

对于test的额外说明,你可以在一个test内设置多个assertion,但是这样不好,应该尽量吧assertion分开,因为如果一个test内,第一个assertion失败,后面的assertion都不会继续执行。

Let`s Go

完整的例子了,书中对应内容Chapter 3

目的:创建一个StatusReport


test "creation of status report with data" do
  assert_difference('StatusReport.count', 1) do
    post :create, :status_report => {
    :project_id => projects(:one).to_param,
    :user_id => users(:one).to_param,
    :yesterday => "I did stuff",
    :today => "I'll do stuff"}
  end
  actual = assigns(:status_report)
  assert_equal(projects(:one).id, actual.project.id)
  assert_equal(users(:one).id, actual.user.id)
  assert_equal(Date.today.to_s(:db), actual.status_date.to_s(:db))
  assert_redirected_to status_report_path(actual)
end

关于上面代码的解释

  1. assert_difference 参数的意思是执行完代码块的结果 StatusReport的conut将会改变1,也就是说 如果执行之前是9 执行之后应该是10
  2. projects(:one)和users(:one) 的意思是指使用fixtures中的数据,暂时理解成使用已有数据,下一个日志会详细介绍一下。
  3. post :create 的意思是模拟一次post请求到create
  4. assigns方法的意思是取出controller里面的实例变量 意味着在controller里有一个@status_report,取出来赋值给acture
  5. assert_equal 字面意思,希望相等
  6. assert_redirected_to 预期的返回结果

执行 rake test:functionals 毫无意外的会失败


1) Error:
    test_creation_of_status_report_with_data(StatusReportsControllerTest):
    ArgumentError: wrong number of arguments (1 for 0)
    /test/functional/status_reports_controller_test.rb:58:in `to_s'
    /test/functional/status_reports_controller_test.rb:58:in
    `test_creation_of_status_report_with_data'

意思是actual.status_date.to_s(:db)这个方法不应该带参数,但其实错误不是这个,而是status_date为nil了 因为nil可以to_s 所以没有报错 而是报参数错误

修改controller里的create


def create
    @status_report = StatusReport.new(params[:status_report])
    @status_report.status_date = Date.today    # ==> the new line
    ## the rest of the method as before
end

测试通过

重构

虽然上面的修改测试通过了,但是我们都知道,rails应该把逻辑放进model里,所以我们准备吧设置status_date的方法放进model

写units测试

  
test "saving a status report saves the status date" do
    actual = StatusReport.new
    actual.save
    assert_equal(Date.today.to_s, actual.status_date.to_s)
 end

修改model

  
class StatusReport < ActiveRecord::Base
    belongs_to :project
    belongs_to :user
    before_save :set_status_date

    def set_status_date
        self.status_date = Date.today
    end
end

上面的测试虽然目前来看是通过了,但是因为before_save在每次edit的时候也会被调用,这样就会修改我们原有的日期,这很明显不是我们想要的,继续重构。
写units测试

  
test "saving a status report that has a date doesn't override" do
    actual = StatusReport.new(:status_date => 10.days.ago.to_date)
    actual.save
    actual.reload
    assert_equal(10.days.ago.to_date.to_s, actual.status_date.to_s)
end

修改model

  
def set_status_date
    self.status_date = Date.today if status_date.nil?
end

测试通过,第一个例子告一段落。

Summary

该书第一部分的目的:了解Rails测试以及相关部分简介。
总结:

  • rails的model测试在units里
  • rails的controller测试在functionals里
  • rails默认使用fixture来生成假数据
  • 在测试过程中,通过之后的重构很重要

你可能感兴趣的:([Rails.Test.Prescriptions]系列之一:0基础的Test & Rails...)