본문 바로가기

Web Programming/Ruby on Rails

Rails Test (feat. TDD를 위한 기반)

개요

 

Rails 에서는 어플리케이션 Test를 위해 기본적으로 Test 모듈(?)들을 제공해준다.

최근에는 Rspec 이 대중적으로 많이 쓰인다고 하지만, Rails 기본 제공 Test 모듈들도 괜찮다는 평이 많은듯 하다(깃헙 눈팅 결과...)

 

(내가 했던 것들이 테스트가 맞나...)

 

사실 웹 어플리케이션 테스트는 유저 Behavior를 시뮬레이션하고 서버 로그를 확인하는 정도로 매번 진행했었다.

그렇지만 그렇게 하면서 매번 정확한 테스트가 될지에 대한 걱정들도 없지 않았고 당연히 적절한 방식이 아니라고 생각했다.

 

Spring 개발환경에서는 무거워서 다방면의 테스트가 쉽지않다는 이야기들도 있었고, 보안상의 DB 접근 등 여러 문제로 쉽지 않았던 것 같다. 어찌됐든, Rails 에서는 테스트 환경을 조금 더 쉽게 제공되고 있기도 하고, TDD(Test Driven Development...)에 익숙해지기 위해 기본적으로 제공되는 테스트 모듈을 사용해보고자 했다.

 


 

Rails test를 위한 셋업

먼저 레일즈 프로젝트들을 생성해주고

$ rails new project

 

scaffold 를 활용해 CRUD 모델을 만들어준다.

저번에도 활용했던 Book 모델이 괜찮은 듯 하여 그대로 사용했다.

$ rails generate scaffold book isbn:bigint name author year:integer price:decimal{7-2} status:boolean genre:integer

 

1
2
3
4
5
6
7
8
9
10
11
12
# db/seeds.rb
100.times do
  Book.create(
    isbn: Faker::Code.isbn,
    name: Faker::Book.title,
    author: Faker::Book.author,
   year: Faker::Number.between(from:1900, to:2019),
   genre: Faker::Number.between(from:1, to:5),
    price: Faker::Commerce.price,
    status: Faker::Boolean.boolean
  )
end
cs

 

그리고 프로젝트 시작을 위한 기본적인 세팅들도 해줬다.

$ bundle install
$ rake db:migrate
$ rake db:migrate

퍼온 이미지

참고로 위와 같이 Webpacker 에러가 났는데, 저번에도 해결할 때 node 버전의 문제로 해결했던 것 같은데 아니었다.

찾아보니 레일즈 6 버전업이 되면서 기본으로 webpacker 가 포함되면서 아래와 같은 명령어로 해결해줘야 한다.

$ rails webpacker:install

파이썬 커뮤니티에서 해결방법을 찾을 수 있었다는게 조금 아이러니 했지만... 간단한 건데도 많이 삐걱거렸다.

 

그리고 테스트 환경을 위한 db:migrate나 console 실행들은 아래와 같이 실행할 수 있다.

$ rake db:migrate RAILS_ENV=test
$ rake db:seed RASILS_ENV=test
$ rails c - e test

Rails Test

rails test

 

Rails는 migrate하는 순간 project/test 디렉토리 내 다양한 part들의 test들을 제공해준다.

먼저 아래는 test 디렉토리 밑에 포함되어 있는 내용들이다.

application_system_test_case.rb  controllers/                     helpers/                         mailers/                         system/
channels/                        fixtures/                        integration/                     models/                          test_helper.rb

여기서 크게 model, controller, helpers, integration 에 대한 테스트들만 확인해보고자 한다.

 

Model 테스트

먼저 Model에 대한 테스트를 진행할 때는, 주로 어떠한 데이터 타입에 대해서 유효한지, 또는 어떤 데이터를 넣었을 때 문제가 될 수 있는지 등을 검사할 수 있다.

 

실제로 사용했던 용도는 String 컬럼 길이에 제한을 두고 이를 넘으면 에러가 제대로 나고 있는지, BigInt 타입에 대한 유효성 검사 등으로 활용했던 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# test/models/book_test.rb
require 'test_helper'
 
class BookTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
 
  setup do
    @book = Book.new(isbn: 123214124,
                     name: "test book name",
                     author: "test yoon author",
                     year: 2020,
                     genre: 4,
                     price: 20000,
                     status: true
    )
  end
 
  # pass
  test "should be valid" do
    assert @book.valid?
  end
 
  # fail
  test "isbn should be integer" do
    invalid_int = [132.71828]
    invalid_int.each do |i|
      @book.isbn = i
      assert_not @book.valid?, "#{i.inspect} is invalid."
    end
  end
 
  # fail
  test "name should be present" do
    @book.name = 23112313
    assert_not @book.valid?
  end
 
  # fail
  test "year should be integer" do
    invalid_year = ["a"2.71828]
    invalid_year.each do |y|
      @book.year = y
      assert_not @book.valid?, "#{y.inspect} is invalid."
    end
  end
end
 
 
cs
rails test test/models/book_test.rb

위와 같은 테스트를 실행하면 콘솔을 통해 결과를 알 수 있다.

 

Controller 테스트

Controller가 수행하는 기능들에 대해서 테스트가 가능하며, Scaffold를 활용하면 기본적인 Controller 테스트가 생성되어 있었다. 

조금 변경하여 크게 많이 사용되는 아래와 같은 내용의 테스트를 생성하고 실행했다.

 

  • 기본적인 CRUD 동작들에 대한 기능 테스트 수행(get, post, patch, put, delete)
  • View에 대한 테스트(특정 elemet 가져오기)
  • 라우팅 테스트(url link name을 줬을 때 적절히 타고 갈 수 있는지)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# test/controller/book_controller.rb
require 'test_helper'
class BooksControllerTest < ActionDispatch::IntegrationTest
  setup do
    @book = books(:one)
  end
  
  # index test
  test "should get index" do
    get books_path
    assert_response :success
  end
 
  # views test
  test "should display home title" do
    get root_path
    assert_select "title""TestProject"
  end
 
  # route test
  test "should get new" do
    get new_book_url
    assert_response :success
  end
  
  # create test
  test "should create book" do
    assert_difference('Book.count'do
      post books_url, params: { book: { author: @bookŒauthor, genre: @book.genre, isbn: @book.isbn, name: @book.name, price: @book.price, status: @book.status, year: @book.year } }
    end
    assert_redirected_to book_url(Book.last)
  end
  
  # route test
  test "should show book" do
    get book_url(@book)
    assert_response :success
  end
  
  # Routing test
  test "should get edit" do
    get edit_book_url(@book)
    assert_response :success
  end
  
  # update test
  test "should update book" do
    patch book_url(@book), params: { book: { author: @book.author, genre: @book.genre, isbn: @book.isbn, name: @book.name, price: @book.price, status: @book.status, year: @book.year } }
    assert_redirected_to book_url(@book)
  end
  
  # delete test
  test "should destroy book" do
    assert_difference('Book.count'-1do
      delete book_url(@book)
    end
    assert_redirected_to books_url
  end
end
 
cs
rails test test/controllers/book_controller.rb

 

Helpers 테스트

Rails Helpes는 Rails View에서 Reusable 또는 Share를 위해 Built-in으로 제공되는 모듈이다.

조금 더 자세히 알고 싶다면 링크 참조

 

주로 아래와 같은 방식으로 활용된다.

1
2
3
4
5
6
7
8
9
require File.dirname(__FILE__) + '/../spec_helper'
 
describe FoosHelper do
 
  it "should do something" do
    helper.some_helper_method.should == @something
  end
 
end
cs
rails test test/helpers/spec_helper.rb

 

Integration 테스트

통합테스트 역시 지원해준다.

해당 명령어를 통해 통합테스트를 생성해줄 수 있다.

rails generate integration_test create_flow

예를 들어 아래와 같이 게시글을 작성에 대한 통합테스트를 아래와 같이 할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
test "can create an article" do
  get "/articles/new"
  assert_response :success
 
  post "/articles",
    params: { article: { title: "can create", body: "article successfully." } }
  assert_response :redirect
  follow_redirect!
  assert_response :success
  assert_select "p""Title:\n  can create"
end
 
cs
rails test test/integration/create_flow.rb

참고