본문 바로가기
SW 개발/Python

Python 단위 테스트(Unit Test)를 위한 unittest 사용법과 예제

by Kibua20 2021. 12. 11.

Python에서 단위 테스트를 unittest 사용법을 설명합니다.  unittest는 Python 기본 Lib로 별도의 모듈을 설치할 필요가 없고, 사용법은 Java의 JUnit과 유사하여 쉽게 사용할 수 있습니다.

 

Python unittest는 테스트 자동화, 자동화를 위한 설정, 종료, 각 테스트 case 실행하고 실행 결과를 report 할 수 있도록 구성되어 있습니다.   unittest는 unittest.TestCase 의 함수를 상속받아 객체 지향적인 방법으로 각각의 함수를 지원합니다. 

  • Test Fixture:  Test를 수행할 때 사전에 필요한 준비와 그와 관련된 동작을 실행합니다. 예를 들어 로그인이 필요한 기능에 대한 로그인을 테스트 전에 사전에 실행하거나, 필요한 데이터를 가져오기, 폴더 생성, DB 연결 등의 동작을 할 수 있습니다.  
  • Test Case: 테스트의 개발 단위입니다. 함수의 리턴 값을 확인하거나, 변수 값을 확인합니다.  
  • Test Suite:  Test Case의 집합입니다.  한 번에 같이 실행해야 할 테스트들을 종합한 결과입니다. 
  • Test Runner:  실제 Test case를 실행하고, 테스트 결과를 사용자에 제공하는 역할의 컴포넌트입니다. 

Python unittest 강좌 동영상을 아래에 자세하게 설명하고 있고, 관련 문서는 unittest를 참고하세요. 

출처; https://www.youtube.com/watch?v=6tNS--WetLI  

 

Python Unittest class 코드 작성

Uniitest code를 작성하는 과정은 아래와 같이 6단계로 구성할 수 있습니다. 

 

  1. 코드 최상단에 unittest lib를 import 합니다.
  2. TestClass 만들고 unittest.TestCase class에서 상속을 받습니다.
  3. @classmethod decorator와 함께  setUpClass(cls)tearDownClass(cls)를 함수를 구현합니다. setUpClass와 tearDownClass은 Class 생성과  소멸 시 호출되는 함수입니다.  @classmethod로 구현하기 때문에 cls를 통해서 class변수를 새로 만들거나 접근할 수 있습니다.  setUpClass(cls)와 tearDownClass(cls)는 생략해도 무방합니다. 
  4. Testcast에 대한 Texture 함수인 setUp(self)와 tearDown(self)를 구현합니다.  setUp(self)와 tearDown(self) 함수는 객체의 값을 self로 통해서 접근 가능합니다. 각  TestCase 함수가 실행할 때마다 앞뒤로 호출됩니다. 
  5. Testcase 함수를 구현합니다.  함수 이름이 test_ 로 시작하면 TestRunner가 알아서 실행해줍니다. 실행 순서는 구현 순서에 상관없이 함수 이름 순서로 실행됩니다.  각각의 Testcase 함수는 독립적으로 실행할 수 있도록 개발해야 합니다.  각 TestCase함수에서는  self.assertEqual(), self.assertInl() 등의 함수를 사용하고 Test 결과의 Pass와 Fail을 결정합니다.  
  6. 마지막으로  unittest.main() 함수를 호출하여 TestRunner를 실행합니다.  

Python Unittest class 코드 작성

Python Unittest 실행

TestCase class를 구현하고 터미널에서 아래와 같이 실행합니다. 

 

$ python3 your_test_cases.py
또는 
$ python3 -m unittest your_test_cases.py 

Python Unittest 실행

 

특정 Test Case만을 실행

python3 -m unittest test_module1 test_module2
python3 -m unittest test_module.TestClass
python3 -m unittest test_module.TestClass.test_method

 

기본 옵션으로는 Test Case 중 하나가 실패하더라도 모든 Test Case함수를 실행하지만, -f --failtest 옵션을 사용하면 첫 번째 실행 중  실행을 중단합니다. 

 

 

Test Case 탐색 (Discovery)

Project 폴더에서 파일명 Pattern을 설정하여 Test Case를 실행할 수 있습니다.  아래 명령어는 project_directory 폴더에서 _test.py를 찾아서 실행합니다.

python -m unittest discover -s project_directory -p "*_test.py"

 

Test Case 실행하지 않기(Skip)

@unittest.skipIf( )를 사용하여 특정 조건에서는 Test case를  실행하지 않을 수 있습니다. 

    @unittest.skipIf(mylib.__version__ < (1, 3),
                     "not supported in this library version")
    def test_format(self):
        # Tests that work for only a certain version of the library.
        pass

 

Test Case 조건문

assertEqual(a, b) a == b  
assertNotEqual(a, b) a != b  
assertTrue(x) bool(x) is True  
assertFalse(x) bool(x) is False  
assertIs(a, b) a is b 3.1
assertIsNot(a, b) a is not b 3.1
assertIsNone(x) x is None 3.1
assertIsNotNone(x) x is not None 3.1
assertIn(a, b) a in b 3.1
assertNotIn(a, b) a not in b 3.1
assertIsInstance(a, b) isinstance(a, b) 3.2
assertNotIsInstance(a, b) not isinstance(a, b) 3.2

 

Sample Code

Test case 를 구현하는 내용을 test_***() 이름의 함수명으로 작성하고 assert 구문으로  성공과 실패를 판단합니다. 소스 코드는 GitHub에 올렸습니다.

import unittest
import sys


class SampleTests(): 
    @classmethod
    def setUpClass(cls):
        "Hook method for setting up class fixture before running tests in the class."
        cls.driver = 'test'
        cls.members = [1,2,3,4]
        print (sys._getframe(0).f_code.co_name)

    @classmethod
    def tearDownClass(cls):
        "Hook method for deconstructing the class fixture after running all tests in the class."    
        print (sys._getframe(0).f_code.co_name)

    def setUp(self):
        "Hook method for setting up the test fixture before exercising it."
        print ('\t',sys._getframe(0).f_code.co_name)

    def tearDown(self):
        "Hook method for deconstructing the test fixture after testing it."
        print ('\t', sys._getframe(0).f_code.co_name)

    def test_runs_1(self):
        print ('\t\t',sys._getframe(0).f_code.co_name, self.driver)
        self.assertTrue(True)

    def test_runs_2(self):
        print ('\t\t',sys._getframe(0).f_code.co_name, self.members)
        self.assertTrue(False)

    def test_line_count(self):
        print ('\t\t',sys._getframe(0).f_code.co_name)
        self.assertTrue(1 == 1)


if __name__ == '__main__':  
    unittest.main()

 

관련 글:

[SW 개발/Python] - Python Decorator를 이용한 함수 실행 시간 측정 방법 (Sample code)

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - Jupyter Notebook의 업그레이드: Jupyter Lab 설치 및 extension 사용법

[개발환경/Web Server] - Web Server 성능 및 Load 측정 Tool: Apache AB (Apache Http Server Benchmarking Tool)

[SW 개발/Python] - Python 여러 버전 설치 방법 (3.x and 3.y 동시 설치)

[SW 개발/Python] - Python 가상환경(Virtual Environment) 만들기 위한 virtualenv 명령어 및 실행 예제

[SW 개발/Python] - Python 정규식(Regular Expression) re 모듈 사용법 및 예제

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - MariaDB의 Python Connector 설치와 사용 방법

[SW 개발/Python] - Python으로 압축 파일 다루기: Zipfile 과 shutil.make_archive()

[개발환경/Web Server] - Python: Web Framework Flask 사용하기




댓글