class StringIndexingTest < Test::Unit::TestCase def setup @string = "foo" end def test_should_return_first_char_ascii_value_for_pos_0 assert_equal ?f, @string[0] end def test_should_return_last_chars_ascii_value_for_pos_neg_1 assert_equal ?o, @string[-1] end def test_should_return_first_two_chars_for_range_0_to_1 assert_equal "fo", @string[-2..-1] end def test_should_return_last_two_chars_for_range_neg_2_to_neg_1 assert_equal "oo", @string[-2..-1] end def test_should_replace_second_char_with_new_substring @string[1] = "id" assert_equal "fido", @string end def test_should_become_empty_when_zero_to_neg_1_set_to_empty_string @string[0..-1] = "" assert @string.empty? end end위의 코드를 봐서는 RSpec과 비교해 봤을 때 크게 다른 점은 없어보인다. 하지만 어떤 하나의 프레임웍이 다른 프레임웍에 비해서 표현력에 있어서는 분명히 더 나은 것을 알 수 있다. 즉 RSpec이 Test::Unit 에 비해서 더 가독성이 있다고 볼 수있다. 베테랑 TDD 개발자로서 필자의 입장에서는 두 가지 모두 읽기 쉽지만, 분명히 인정해야 할 것은 Test::Unit 쪽이 처음 접근하는데 있어서 알아야 할 것이 좀 더 있다는 것이다.
assert_equal "fido", @string"Expected first_value, but god second_value"라는 메시지를 여러 차례 보다보면, 다음과 같은 형태로 함수가 호출된다고 볼 수도 있다.
assert_equal expected, actual위 문장에서 expected와 actual이 뒤바뀌게 될 경우 자칫 혼란을 가져올 수 있는데, RSpec에서는 이런 점을 미리 방지하고 있다.
actual.should == expected우리가 사용하고 있는 자연 언어의 형태를 취하고 있는데, 마치 "온도는 75도여야 합니다(The temperature should equal 75 degrees" 라고 말하는 것과 비슷하다.
>> a = "foo" => "foo" >> a.equal?("foo") => false >> a.equal?(a) => true >> a == "foo" >> => true하지만 위에서 보듯이 equal? 연산자는 두 값을 가진 오브젝트의 동질성을 평가하는데 사용되는 연산자이다. Test::Unit에서는 위 연산자를 위해서 assert_same 이라는 명령을 제공하고 있지만, 처음 접하는 사용자에게는 접근하기가 쉽지가 않다.
class MyClass def eql?(other) true end end if __FILE__ == $PROGRAM_NAME require "test/unit" class MyTest < Test::Unit::TestCase def test_my_class_equality a = MyClass.new b = MyClass.new assert_equal a,b end end end위의 테스트는 실패한다. 그런 이제 == 를 테스트 해보자.
class MyClass def ==(other) true end end그럼 테스트는 성공적으로 종료된다. 이 결과를 통해서 assert_equal(expected, actual)은 actual.should == expected와 동일하다는 것을 알 수 있다.
describe "meta-test for equality expectations" do it "should easily allow testing different kinds of equality" do a = MyClass.new b = MyClass.new a.should == b a.should eql(b) end end이 걸로 또 RSpec이 좀 더 투명하고 접근하기 쉽다는 것을 알 수 있다.
$ spec -f s string_spec.rb String indexing - should return first character"s ASCII value for position 0 - should return the last character"s ASCII value for position -1 - should return the first two characters for the range 0..1 - should return the last two characters for the range -2..-1 - should replace the second character with a new substring - should become empty when 0..-1 is set to empty string Finished in 0.009989 seconds 6 examples, 0 failures실제로 스펙 코드를 보지 않고서도 이전에 작성했던 테스트에 대한 설명을 얻을 수가 있다. 이렇게 확인하는 과정을 통해서 실제로 원하는 모든 테스트를 다 수행하고 있는지에 대해서도 확인할 수가 있다. 또 테스트에 대한 문구가 이상하게 되어 있을때는 보통 테스트 자체가 잘못되어 있다는 것을 확인할 수가 있다. 이런 확인과정이 모든 문제를 해결하기 위한 방법이 될 수는 없지만, 최소한 전체 코드를 한 번에 살펴볼 수 있는 방법으로 활용될 수도 있다.
$ spec -f r string_spec.rb # String indexing # * should return first character"s ASCII value for position 0 # * should return the last character"s ASCII value for position -1 # * should return the first two characters for the range 0..1 # * should return the last two characters for the range -2..-1 # * should replace the second character with a new substring # * should become empty when 0..-1 is set to empty string또 HTML 형태로도 만들어낼 수 있는데, HTML형태가 가장 보기에 좋아보인다. 여기서 실패가 발생하도록 한 스펙파일의 실행결과를 보자.
$ spec -f h string_spec.rb
describe "String case manipulations" do before :each do @string = "fOo" end it "should be converted to all caps when #upcase is sent" do @string.upcase.should == "FOO" end it "should start with a capital letter followed by lowercase letters when #capitalize is sent" do @string.capitalize.should == "Foo" end it "should be converted to all lowercase when #downcase is sent" do @string.downcase.should == "foo" end end역시 마찬가지로 각 구문에서는 "before :each"라는 훅을 설정해서 각 예제를 실행할때마다 필요한 초기화를 수행하도록 하고 있다. 이제 스펙을 실행해 보면 우리가 배우기 위해서 그 동안 작성했던 스펙들이 String 클래스의 동작을 얼마만큼이나 잘 묘사하고 있는지를 볼 수 있다.
$ spec -f s string_spec.rb String indexing - should return first character"s ASCII value for position 0 - should return the last character"s ASCII value for position -1 - should return the first two characters for the range 0..1 - should return the last two characters for the range -2..-1 - should replace the second character with a new substring - should become empty when 0..-1 is set to empty string String case manipulations - should be converted to all caps when #upcase is sent - should start with a capital letter followed by lowercase letters when #capitalize is sent - should be converted to all lowercase when #downcase is sent Finished in 0.012205 seconds 9 examples, 0 failures여기서부터 여러분 스스로 더 예제를 추가해 봐도 좋고, 아니면 다음 글에서 조금 더 고급 주제를 다룰 때까지 다른 걸 하면서 기다려도 괜찮다.
최신 콘텐츠