이번 글에서는 지금까지 살펴본 Minitest를 사용해서 '회문 검사 프로그램 만들기'와 '암호화 프로그램 만들기' 글에서 작성한 프로그램에 대한 테스트 코드를 작성해 보도록 하겠다.
아래 코드를 보면 Minitest를 사용하여 테스트 코드를 작성했는데, 두 개의 테스트 메서드에서 각각 reverse_xxx 메서드와 palindrome? 메서드를 검증하는 코드를 작성했다.
test_palindrome 메서드를 보면 refute라는 처음 보는 어설션 메서드가 보이는데, 이것은 assert와 달리 어떤 객체 또는 식의 결괏값이 거짓으로 평가되는지 검증한다.
require 'minitest/autorun'
require '../palindrome'
class Test < Minitest::Test
def test_reverse
assert_equal "ybur", reverse_by_append("ruby")
assert_equal "ybur", reverse_by_insert("ruby")
end
def test_palindrome
refute palindrome?("ruby")
assert palindrome?("level")
assert palindrome?("noon")
assert palindrome?("refer")
end
end
아래 그림을 보면 refute로 시작하는 어설션 메서드가 여러 개 보이는데 이전 글에서 살펴본 assert로 시작하는 메서드와 짝을 이룬다.
테스트 코드 실행을 위해 먼저 D:/blog/ruby/palindrome 폴더 아래 test 폴더를 만들고 그 안에 test.rb 파일을 만들어 앞의 코드를 저장하자.
그리고 test 폴더의 위치에서 cmd 창을 열어 아래 그림처럼 test.rb 파일을 실행해 보자.
2개의 테스트 메서드가 실행되었고 그 안에 작성된 6개의 검증 구문이 모두 성공했다고 결과가 나온다.
이번에는 암호화 기능을 테스트하는 코드를 작성해 보자.
아래는 ReplaceEncryptor 클래스를 사용한 암호화 및 복호화 기능을 테스트하는 코드이다.
setup 훅(Hook)을 사용해서 ReplaceEncryptor 클래스의 객체를 생성해서 인스턴스 변수에 할당하였고 attr_reader를 사용하여 해당 인스턴스 변수에 접근하는 getter를 만들었다.
그리고 test_encrypt 메서드에서는 getter를 통해 ReplaceEncryptor 클래스의 객체를 접근하여 해당 객체의 encrypt와 decrypt 메서드를 테스트하는 검증 구문을 작성하였다.
이때 refute_equal 어설션을 사용하여 암호화 후의 문자열이 암호화 전의 문자열과 다름을 검증하는 코드를 작성하였다.
require 'minitest/autorun'
require '../replace_encryptor'
class TestReplaceEncryptor < Minitest::Test
attr_reader :encryptor
def setup
@encryptor = ReplaceEncryptor.new
end
def test_encrypt
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
enc_str = encryptor.encrypt(str)
refute_equal str, enc_str
dec_str = encryptor.decrypt(enc_str)
assert_equal str, dec_str
end
end
테스트 코드 실행을 위해 먼저 D:/blog/ruby/encryption 폴더 아래 test 폴더를 만들고 그 안에 test_replace_encryptor.rb 파일을 만들어 앞의 코드를 저장하자.
그리고 아래 그림처럼 cmd 창에서 test.rb 파일을 실행해 보자.
1개의 테스트 메서드가 실행되었고 그 안에 작성된 2개의 검증 구문이 모두 성공했다고 결과가 나온다.
다음으로 아래 코드처럼 ShiftEncryptor 클래스를 테스트하는 코드를 작성해 보자.
ShiftEncryptor 클래스는 문자의 위치를 이동시키는 방법으로 암호화를 하는데, 0 또는 대상 문자열의 길이만큼 문자를 이동한다면 실제로는 이동을 하지 않는 것과 같기 때문에 대상 문자열과 암호화 후의 문자열은 같아야 한다.
test_encrypt 메서드를 보면 이 두 가지에 대한 검증 코드를 볼 수 있다.
require 'minitest/autorun'
require '../shift_encryptor'
class TestShiftEncryptor < Minitest::Test
attr_reader :encryptor
def setup
@encryptor = ShiftEncryptor.new
end
def test_encrypt
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
enc_str = encryptor.encrypt(str, 0)
assert_equal str, enc_str
enc_str = encryptor.encrypt(str, str.length)
assert_equal str, enc_str
enc_str = encryptor.encrypt(str, str.length / 2)
refute_equal str, enc_str
dec_str = encryptor.decrypt(enc_str, str.length / 2)
assert_equal str, dec_str
end
end
테스트 코드 실행을 위해 먼저 D:/blog/ruby/encryption/test 폴더 아래 test_shift_encryptor.rb 파일을 만들어 앞의 코드를 파일에 저장하고 cmd 창에서 해당 파일을 실행시켜 보자.
작성한 검증 구문 4개가 모두 성공했다고 결과가 나온다.
이번에는 BlockShiftEncryptor 클래스를 테스트하는 코드를 작성해 보자.
BlockShiftEncryptor 클래스 역시 ShiftEncryptor 클래스처럼 문자의 위치를 이동시키는 방법을 사용해 암호화하는 것은 같지만, 한 가지 다른 점은 대상 문자열을 특정 크기의 블록으로 나누고 각각의 블록 내에서 문자를 이동시킨다는 것이다.
그래서 대상 문자열의 특정 블록을 ShiftEncryptor 클래스를 사용하여 같은 이동 크기로 암호화하면 BlockShiftEncryptor 클래스를 사용해 암호화한 문자열에서의 해당 블록과 같은 (암호화된) 문자열을 얻게 된다.
이것을 검증하기 위한 코드를 테스트 메서드에서 볼 수 있다.
require 'minitest/autorun'
require '../shift_encryptor'
require '../block_shift_encryptor'
class TestBlockShiftEncryptor < Minitest::Test
attr_reader :encryptor
def setup
@encryptor = BlockShiftEncryptor.new(5, 3)
end
def test_encrypt
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
enc_str = encryptor.encrypt(str)
refute_equal str, enc_str
assert_equal enc_str[0..4], ShiftEncryptor.new.encrypt(str[0..4], 3)
dec_str = encryptor.decrypt(enc_str)
assert_equal str, dec_str
end
end
테스트 코드 실행을 위해 같은 폴더에 test_block_shift_encryptor.rb 파일을 만들어 앞의 코드를 저장하고 해당 파일을 실행시켜 보자.
작성한 검증 구문 3개가 모두 성공했다는 결과를 볼 수 있다.
이어서 XorEncryptor 클래스를 테스트하는 코드를 작성해 보자.
아래 test_encrypt 메서드를 보면 단순히 암호화와 복호화를 검증하는 코드뿐만 아니라 암호화할 때와 다른 비밀번호로는 복호화가 제대로 되지 않음을 검증하는 코드도 볼 수 있다.
require 'minitest/autorun'
require '../xor_encryptor'
class TestXorEncryptor < Minitest::Test
attr_reader :encryptor
def setup
@encryptor = XorEncryptor.new
end
def test_encrypt
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
enc_str = encryptor.encrypt(str, "1234!")
refute_equal str, enc_str
dec_str = encryptor.decrypt(enc_str, "1234")
refute_equal str, dec_str
dec_str = encryptor.decrypt(enc_str, "1234!")
assert_equal str, dec_str
end
end
테스트 코드 실행을 위해 같은 폴더에 test_xor_encryptor.rb 파일을 만들어 앞의 코드를 저장하고 해당 파일을 실행시켜 보자.
역시 작성한 검증 구문이 모두 성공했다는 결과를 볼 수 있다.
그런데 앞서 만든 4개의 테스트 파일을 각각 실행하지 않고 한 번에 실행하려면 어떻게 해야 할까?
방법은 간단하다. 테스트 파일이 들어 있는 폴더(D:\blog\ruby\encryption\test) 안에 test.rb 파일을 하나 만들고 그 파일에 아래 코드를 작성해서 저장한 다음 test.rb 파일을 실행하면 된다.
코드를 보면 Dir 클래스의 glob 클래스 메서드를 사용하여 __dir__ 이 나타내는 폴더 아래에 있는 'test_'로 시작하는 모든 루비 파일을 로드하는 게 전부다.
__dir__ 은 루비의 특수 상수(Special Constants) 중 하나로 현재 소스 파일이 위치한 디렉터리 경로를 나타낸다.
Dir.glob(File.join(__dir__, "test_*.rb")).each { |f| require f }
그럼 cmd 창을 열어 test.rb 파일을 실행한 후 결과를 직접 확인해 보자.
4개의 테스트 파일에 작성된 테스트 메서드 4개와 그 안에 작성한 12개의 검증 구문이 모두 실행된 걸 볼 수 있다.
만약 여러 개의 테스트 파일 중 어떤 테스트 파일 안의 검증이 실패할 경우에는 결과가 어떻게 나올까? 직접 확인해 보기 위해 아래 코드를 같은 폴더 안에 test_fail.rb 파일로 저장하고 다시 test.rb를 실행해 보자.
require 'minitest/autorun'
class TestFail < Minitest::Test
def test_fail
assert_same "Ruby", "Ruby"
end
end
테스트 실행 결과를 보면 test_fail.rb 파일 안에 있는 TestFail 클래스의 test_fail 메서드가 실패했다고 잘 보여준다.
그리고 아래 메시지 내용을 보면 assert_same 어설션을 사용한 검증이 실패했음을 알 수 있는데, 친절하게도 object_id 값까지 함께 보여준다.
See you again~~