AWDR 阅读笔记 – 3.Code – Task B

Agile Web Development with Rails 5.1 阅读笔记系列第 3 篇,表单验证和简单单元测试,参考本书该部分(CH7 – Task B)。

Validating!

Rails 已集成表单验证,只需要在 Model 中使用 validates 方法指明即可。

class Product < ApplicationRecord
  # presence: true tells the validator to check that each of the named fields is present and that its contents aren’t empty.
  validates :title, :description, :image_url, presence: true

  validates :title, uniqueness: true
  validates :image_url, allow_blank: true, format: {
    with:     %r{.(gif|jpg|png)Z}i,
    message:  'must be a URL for GIF, JPG or PNG image.'
  }
  validates :price, numericality: { greater_than_or_equal_to: 0.01 }
end

其中:

  1. option – presence 指定该字段不为空;
  2. option – uniqueness 指定该字段为唯一字段;
  3. option – format 指定正则表达式;
  4. option – numericality 指定字段为数值类型,且需要满足特定条件。

Unit Testing of Models

直接看书中完整代码。

test/models/product_test.rb

require 'test_helper'

class ProductTest < ActiveSupport::TestCase
  fixtures :products

  test "product attributes must not be empty" do
    product = Product.new
    assert product.invalid?
    assert product.errors[:title].any?
    assert product.errors[:description].any?
    assert product.errors[:price].any?
    assert product.errors[:image_url].any?
  end

  test "product price must be positive" do
    product = Product.new(title:        "My Book Title",
                          description:  "yyy",
                          image_url:    "zzz.jpg")
    product.price = -1
    assert product.invalid?
    assert_equal ["Must be greater than or equal to 0.01"],
      product.errors[:price]

    product.price = 0
    assert_equal ["Must be greater than or equal to 0.01"],
      product.errors[:price]

    product.price = 1
    assert product.valid?
  end

  def new_product(image_url)
    Product.new(title:       "My Book Title",
                description: "yyy",
                price:       1,
                image_url:   image_url)
  end

  test "image url" do
    ok = %w{ fred.gif fred.jpg fred.png FRED.JPG FRED.Jpg
             http://a.b.c/x/y/z/fred.gif }
    bad = %w{ fred.doc fred.gif/more fred.gif.more }

    ok.each do |image_url|
      assert new_product(image_url).valid?,
             "#{image_url} shouldn't be invalid"
    end

    bad.each do |image_url|
      assert new_product(image_url).invalid?,
             "#{image_url} shouldn't be valid"
    end
  end

  test "product is not valid without a unique title" do
    product = Product.new(title:       products(:ruby).title,
                          description: "yyy",
                          price:       1,
                          image_url:   "fred.gif")

    assert product.invalid?
    assert_equal ["has already been taken"], product.errors[:title]
  end

  test "product is not valid without a unique title - i18n" do
    product = Product.new(title:       products(:ruby).title,
                          description: "yyy",
                          price:       1,
                          image_url:   "fred.gif")

    assert product.invalid?
    assert_equal [I18n.translate('errors.messages.taken')],
                 product.errors[:title]
  end
end

无论使用什么数据库,一个 Rails APP 数据库均被分为三个:

  1. development;
  2. production;
  3. test。

单元测试模块使用的数据库即为 test 数据库,测试前可以预先编写 fixtures 数据,比如说这里的 Products fixtures 数据即 test/fixtures/products.yml

product_test.rb 中的开头,我们就指明了 products.yml 为数据来源。

test/fixtures/products.yml

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  title: MyString
  description: MyText
  image_url: lorem.jpg
  price: 9.99

two:
  title: MyString
  description: MyText
  image_url: lorem.jpg
  price: 9.99

ruby:
  title: Programing Ruby 1.9
  description:
    Ruby is the fastest growing and most exciting dynamic
    language out there. If you need to get working programs
    delivered fast, you should add Ruby to your toolbox.
  price: 49.50
  image_url: ruby.jpg

test 方法用于描述单元测试,assert_* 断言即测试中的判断。

上述测试涉及:

  1. 空表单;
  2. price;
  3. image;
  4. title。

其中的 title 使用到了 fixtures 中的数据。

Unit Testing of Controllers

这里,Scaffold 已经为我们生成了 Products controller 的单元测试,不过实际上由于我们设置了 title uniqueness,所以这里的 title 要使用随机值。

test/controllers/products_controller_test.rb

class ProductsControllerTest < ActionDispatch::IntegrationTest
  setup do
    @product = products(:one)
    @title = "The Great Book #{rand(1000)}"
  end
  # ...
end

Other Useful CMD

Test Models

$ bin/rails test:models

Test All

$ bin/rails test

Test DB CLI

$ bin/rails dbconsole -e test

参考:

  1. Agile Web Development with Rails 5.1 . Sam Ruby. David Bryant Copeland. with Dave Thomas. 2017 The Pragmatic Programmers, LLC.
  2. 完整源码见:https://media.pragprog.com/titles/rails51/code/rails51/depot_c/*
  3. https://api.rubyonrails.org/classes/ActiveSupport/TestCase.html
  4. http://docs.seattlerb.org/minitest/

作者: V

Web Dev

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s