PMPV
assert equal은 어디에서 나온 놈일까? 본문
오늘은 어피치 초롱
ruby koan에서 assert_equal은 어떻게 써야하는 놈일까?
어디에서 나왔을까?
assert_raise는 또 뭐고, 어떤 놈들이 있을까?
1.
def test_accessing_hashes_with_fetch
hash = { :one => "uno" }
assert_equal "uno", hash.fetch(:one)
assert_raise(__) do
hash.fetch(:doesnt_exist)
end
# THINK ABOUT IT:
#
# Why might you want to use #fetch instead of #[] when accessing hash keys?
end
module Assertions
FailedAssertionError = Class.new(StandardError)
def flunk(msg)
raise FailedAssertionError, msg
end
def assert(condition, msg=nil)
msg ||= "Failed assertion."
flunk(msg) unless condition
true
end
def assert_equal(expected, actual, msg=nil)
msg ||= "Expected #{expected.inspect} to equal #{actual.inspect}"
assert(expected == actual, msg)
end
def assert_not_equal(expected, actual, msg=nil)
msg ||= "Expected #{expected.inspect} to not equal #{actual.inspect}"
assert(expected != actual, msg)
end
def assert_nil(actual, msg=nil)
msg ||= "Expected #{actual.inspect} to be nil"
assert(nil == actual, msg)
end
def assert_not_nil(actual, msg=nil)
msg ||= "Expected #{actual.inspect} to not be nil"
assert(nil != actual, msg)
end
def assert_match(pattern, actual, msg=nil)
msg ||= "Expected #{actual.inspect} to match #{pattern.inspect}"
assert pattern =~ actual, msg
end
def assert_raise(exception)
begin
yield
rescue Exception => ex
expected = ex.is_a?(exception)
assert(expected, "Exception #{exception.inspect} expected, but #{ex.inspect} was raised")
return ex
end
flunk "Exception #{exception.inspect} expected, but nothing raised"
end
def assert_nothing_raised
begin
yield
rescue Exception => ex
flunk "Expected nothing to be raised, but exception #{exception.inspect} was raised"
end
end
end
def assert_equal(expected, actual, msg=nil)
msg ||= "Expected #{expected.inspect} to equal #{actual.inspect}"
assert(expected == actual, msg)
end
module Assertions
FailedAssertionError = Class.new(StandardError)
def flunk(msg)
raise FailedAssertionError, msg
end
def assert(condition, msg=nil)
msg ||= "Failed assertion."
flunk(msg) unless condition
true
end
쉽게 생각하자면, 우리가 소스코드를 작성하면서 마주치는 에러들, 예를 들면 NoMethodError, TypeError, ArgumentError 등이 있다. 이 에러 메세지는 수시로 나오는 에러이고, 이 에러가 발생했을 때의 처리를 항상 똑같이 하기 위해 에러를 하나로 묶었다고 생각하면 쉽겠다. 그 묶음이 standard error이다. 모듈의 첫 번째 줄은 이 프로그램에서는 FailedAssertionError 이라는 에러가 자주 발생할 것 같으니 스탠다드 에러의 꼭지를 하나 추가해서 여기서 따로 정의한다고 생각하자.
그럼 그 다음 액션부터는 발생 예상되는 에러의 처리를 정의한 것이다. 첫 줄의 flunk는 FailedAssertionError를 발생시켜주고, 인자로 받는 놈을 메세지로 전달해준다. irb를 켜서 저 소스를 옮겨적으면 이렇게 나온다.
:001 > FailedAssertionError = Class.new(StandardError)
=> FailedAssertionError
:002 > def flunk(msg)
:003?> raise FailedAssertionError, msg
:004?> end
=> :flunk
:005 > flunk "hello"
FailedAssertionError: hello
from (irb):3:in `flunk'
from (irb):5
from /Users/p/.rvm/rubies/ruby-2.3.3/bin/irb:11:in `<main>'
flunk 함수를 정의해주고 인자로 "hello"를 전달해주었다. 그러자 오류 메세지에 FailedAssertionError: hello가 뜨는 것을 볼 수 있다.
이런 식으로 assert 함수도 따로 정의해준다. assert 함수는 하나의 인자를 받고, 동시에 메세지를 받는다.
assert_equal 함수에서, 마지막에
assert (expected 와 actual는 같다, 위에서 할당된 msg)
이런 내용이 있었다. 여기선 expected == actual의 값을 assert 함수에 인자로 전달한 모습이다.
만약 두 개의 인자가 같지 않다면, assert_equal은 false를 assert에 전달해주고, 메세지를 같이 전달해준다.
그럼 assert 함수는 flunk 함수를 불러오고, FailedAssertionError을 불러오게 된다.
복잡복잡
그럼 assert_equal에서,
assert (expected 와 actual는 같다, 위에서 할당된 msg)
expected와 actual이 같다면? assert에 전달되는 값은 true일것이다.
assert 함수는 false가 아닌 true를 인자로 받을 때, 그대로 true를 반환시킨다.
다시 처음으로 돌아가보자.
def assert_equal(expected, actual, msg=nil)
msg ||= "Expected #{expected.inspect} to equal #{actual.inspect}"
assert(expected == actual, msg)
end
assert_equal 함수는 expected와 actual이 같아야 한다.
두 값이 다르다면, false를 전달해주고 최종적으로 FailedAssertionError 에러 메세지를 뽑아낸다.
그럼 다른 함수는?
def assert_not_equal(expected, actual, msg=nil)
msg ||= "Expected #{expected.inspect} to not equal #{actual.inspect}"
assert(expected != actual, msg)
end
assert_not_equal은 expected와 actual이 달라야 한다.
def assert_nil(actual, msg=nil)
msg ||= "Expected #{actual.inspect} to be nil"
assert(nil == actual, msg)
end
def assert_not_nil(actual, msg=nil)
msg ||= "Expected #{actual.inspect} to not be nil"
assert(nil != actual, msg)
end
이 두 함수 역시 예상 가능하게, actual이 nil 값을 가지거나, 가지지 않아야 한다.
def assert_raise(exception)
begin
yield
rescue Exception => ex
expected = ex.is_a?(exception)
assert(expected, "Exception #{exception.inspect} expected, but #{ex.inspect} was raised")
return ex
end
flunk "Exception #{exception.inspect} expected, but nothing raised"
end
assert_raise 함수는 좀 더 복잡한 모양새를 하고 있다. 차근차근 읽어보자.
인자로는 exception을 받는다.
이 부분은 예외 처리에 관한 내용이다. 따로 포스팅을 해야겠다.
쉽게 생각하자면, assert_raise는 다음의 블록이 불러오는 오류의 종류를 맞추는 함수라고 생각하면 되겠다.
다시 처음으로 돌아가서, assert_raise의 활용을 보자.
def test_accessing_hashes_with_fetch
hash = { :one => "uno" }
assert_equal "uno", hash.fetch(:one)
assert_raise(__) do
hash.fetch(:doesnt_exist)
end
# THINK ABOUT IT:
#
# Why might you want to use #fetch instead of #[] when accessing hash keys?
end
네 번째 줄에서 assert_raise가 나온다. hash는 {:one => "uno"}라는 자료 구조를 가지고 있고, 여기에서 :doesnt_exist, 말 그대로 존재하지 않는 키로 접근했을 땐 어떻게 될까? 물론 오류가 나올 것이다. 어떤 오류?
Exception FillMeInError expected, but #<KeyError: key not found: :doesnt_exist> was raised
예외 ___가 예상되었지만, KeyError: key not found가 발생하였습니다.
다른 assert_raise의 활용도 같이 보자.
def my_global_method(a,b)
a + b
end
def test_calling_global_methods_with_wrong_number_of_arguments
exception = assert_raise(___) do
my_global_method
end
assert_match(/__ /, exception.message)
exception = assert_raise(___) do
my_global_method(1,2,3)
end
assert_match(/__ /, exception.message)
end
about_methods의 35번째 라인에 나오는 문제이다.
my_global_method는 두 개의 인자를 받아서 더해주는 간단한 함수인데, 이 두 가지 상황에서는 인자를 받지 않았거나, 잘못된 수의 인자를 받을 때를 가정하고 있다. 그럼 어떤 오류가 날까?
Exception FillMeInError expected, but #<ArgumentError: wrong number of arguments
(given 0, expected 2)> was raised
예외 ___가 예상되었지만, ArgumentError: wrong number of arguments가 발생하였습니다.
이렇게 오류의 이름을 알려주면, 공백에는 오류의 이름을 채워넣으면 된다.
알아보는 김에 assert_match까지 알아보자.
def assert_match(pattern, actual, msg=nil)
msg ||= "Expected #{actual.inspect} to match #{pattern.inspect}"
assert pattern =~ actual, msg
end
이 부분은 정규 표현식에 관한 내용이다. 이 부분도 따로 정리를 해서 포스팅을 하고, 지금은 활용 방법만 알아보자.
실제 문제 사례를 보자.
def test_calling_global_methods_with_wrong_number_of_arguments
exception = assert_raise(ArgumentError) do
my_global_method
end
assert_match(/__/, exception.message)
exception = assert_raise(ArgumentError) do
my_global_method(1,2,3)
end
assert_match(/__/, exception.message)
end
Expected "wrong number of arguments (given 0, expected 2)" to match /'___ '/
/__/ 다음엔 예외 메세지를 인자로 받는다.
여기선 __에 예외 메세지 문구를 적어주면 된다.
assert_match(/wrong number of arguments/, exception.message)
이렇게.
오늘의 포스팅은 여기까지. 아무래도 루비 코안 가이드 버전 투를 작성해야할듯
'Ruby on Rails > ruby koan' 카테고리의 다른 글
ruby koan 가이드 v.2 (0) | 2018.02.06 |
---|---|
inspect와 to_s는 뭐가 다른걸까? (0) | 2018.01.27 |
루비에서 object id는 왜 홀수일까? (0) | 2018.01.22 |