[QA] API - Automation Testing (unittest, requests htmlTestRunner, SlackAPI)

2023. 6. 22. 17:33IT관련

728x90
반응형

 

 

1. Test Case 설계 

이 단계에서는 테스트할 케이스를 설계 합니다.

단순히 케이스를 생성하는 것이 아닌 추후 쉬운 수정이 될 수 있게 설계를 해야 합니다.

API의 경우에는 같은 API라고 할지라도 새롭게 추가되는 에러코드라던지의 정보들이 빠르게 변할 수 있어서

Test Case를 설계할 때 이를 유의해서 작성을 합니다.

import unittest

class MyTestCase(unittest.TestCase):
    def setUp(self):
        # 테스트 케이스 실행 전에 수행되는 설정 코드
        pass

    def tearDown(self):
        # 테스트 케이스 실행 후에 수행되는 정리 코드
        pass

    def test_something(self):
        # 테스트하고자 하는 기능 또는 시나리오에 대한 테스트 코드 작성
        # self.assertEqual(expected, actual)과 같은 어설션(assertion) 메서드를 사용하여 테스트 수행
        pass

    def test_another_thing(self):
        # 다른 기능 또는 시나리오에 대한 테스트 코드 작성
        pass

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

일반적으로는 이런 형태로 구성이 되고 test_ 를 메서드 이름에 포함하여 테스트 하고자 하는 기능들을 설계합니다.

여러 개의 Test Case 들을 구성할 때 위와 같은 형태로 만들고, 필요한 함수들은 이름에 test_ 를 포함하지 않고 개발하면 실행되지 않습니다.

 

 

2. Test Suite 설계

API 단위로 잘 쪼개져 개발되어있는 Test Case 들을 묶어 줍니다. 

이렇게 해서 큰 단위의 Test Suite 들을 unittest 시 묶음으로 수행시킬 수 있습니다.

import unittest

# Test Suite 클래스 생성
class MyTestSuite(unittest.TestSuite):
    def __init__(self):
        super().__init__()

        # 테스트 케이스 그룹화
        self.addTest(MyTestCase('test_something'))
        self.addTest(MyTestCase('test_another_thing'))

if __name__ == '__main__':
    # Test Suite 실행
    suite = MyTestSuite()
    unittest.TextTestRunner().run(suite)

이런 식으로도 구성하여 실행할 수 있는데 이 설명단계에서는 구체적인 설명보다는 형식을 주로 작성합니다.

 

3. Test Suite 수행 

위에서 만들어진 각각의 Test Suite 들을 unittest의 TextTestRunner() 를 사용하여 묶음된

Test Suite를 수행할 수 있습니다.

또는 Test 의 가시성을 위해서 좀 더 HtmlTestRunner를 통해서 묶음된 Test Suite를 수행할 수 있습니다.

# TestSuite 객체 생성 및 테스트 케이스 추가
suite = unittest.TestSuite()
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(YourTestCaseClass))

# TestSuite 실행 및 결과 저장
result = unittest.TextTestRunner().run(suite)

 

4. Test Result Report 공유 

unittest 자체에서 지원하는 TextTestRunner()의 경우에는 아래의 경우대로 결과확인을 할 수가 있는데

텍스트 형태다보니 공유하기가 쉽지 않습니다.

print("Total tests run:", result.testsRun)
print("Total failures:", len(result.failures))
print("Total errors:", len(result.errors))

 

 

 

이런 일련의 과정을 통해서 Unittest 를 이용하여 Code 들을 자동화할 수 있습니다.

이런 과정들을 좀더 상세하게 각각의 회사 Product Service에 맞게 적절하게 변경을 하면 

효율적인 QA Automation 작업을 할 수 있습니다.

 

 

이번엔 실제로 HtmlTestRunner & UnitTest & Requests 를 이용한 API Test 를 자동화하는 간단한 샘플을 작성해봅니다.

 

1. Test Case 설계 

이 단계에서 저는 가장 중요하게 생각했던 건 API를 설계할 때 구조가 어떻게 되는지, 실제 클라이언트에 전달될 때는 어떤 형태로 전달이 되는지를 먼저 파악해보고 결정을 했습니다.

결과로 저는 하나의 API에 확인해야 할 에러코드가 많고 설계할 때도 특정 Status_code 를 추가하는 방식으로 개발을 하기 때문에 Test Case 단위를 Status_code 단위로 작성을 하였습니다.

# sunbaeVerificateFile.py
import os
import unittest
import sys
from functions import post_message_to_api

# 현재 파일의 경로에서 상위 디렉토리로 이동한 경로를 구합니다.
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)

# 상위 디렉토리를 검색 경로에 추가합니다.
sys.path.append(parent_dir)
from utils.property import QAProperties

class SunbaeVerificate(unittest.TestCase):
   
    def setUp(cls) -> None:
        return super().setUp()

    def test_ACCOUNT_DOES_NOT_EXIST(self):
        expectStatusCode = 400
        caseName = "ACCOUNT_DOES_NOT_EXIST"
        body = {
            "email":"adf@df.co",
            "password":"test"
        }
        post_message_to_api(
            self, 
            caseName,
            expectStatusCode,
            self.url,
            body)

    def test_INVALID_PASSWORD(self):
        caseName = "INVALID_PASSWORD"
        expectStatusCode = 400
        body = {
            "email":"tnsqo1126@naver.com",
            "password":"asdf"
        }
        post_message_to_api(
            self, 
            caseName,
            expectStatusCode,
            self.url,
            body)
        
    def tearDown(self) -> None:
        return super().tearDown()

request 쏘는 부분은 함수를 만들어서 테스트 결과를 유용하게 사용할 수 있게 개발을 하였습니다.

2. Test Suite 설계

이렇게 만들게 된 Test Case 들은 unittest의 TestLoader() 를 통해서 가져온 후 

unittest의 TestSuite() 메서드를 통해서 Case들을 묶을 수 있습니다. 

이것을 통해서 여러 개의 Test Case 를 통합시킬 수 있습니다.

# main.py
import unittest
import os
from sunbaeVerificateFile import sunbaeVerificate
import HtmlTestRunner
import glob


if __name__ == '__main__':

    try:
        sunbae_TestCase1 = unittest.TestLoader().loadTestsFromTestCase(sunbaeVerificate)
        suite = unittest.TestSuite(
                [sunbae_TestCase1])
        # 여러 개의 경우 [sunbae_TestCase1,sunbae_TestCase2]
        
    except Exception as e:
        print(e)

 

3. Test Suite 수행 

위에서 만들어진 각각의 Test Suite 들을 HtmlTestRunner()  사용하여 묶음된 Test Suite를 수행합니다.

runner 에 여러가지 옵션들이 있는데 가독성이 좋게 레포트를 내고 싶을 때 자기에 맞게 커스텀을 하면 될 것 같습니다.

아래 코드가 실행이 되면 콘솔에서 실행결과들이 나오고, 실제 테스트가 완료가 되면 html 파일 형식으로 지정된 경로에 생성이 됩니다.

# main.py
import unittest
import os
from sunbaeVerificateFile import sunbaeVerificate
import HtmlTestRunner
import glob


if __name__ == '__main__':

    try:
        sunbae_TestCase1 = unittest.TestLoader().loadTestsFromTestCase(sunbaeVerificate)
        suite = unittest.TestSuite(
                [sunbae_TestCase1])
        # 여러 개의 경우 [sunbae_TestCase1,sunbae_TestCase2]
        
        runner = HtmlTestRunner.HTMLTestRunner(
            output='api_results',
            verbosity=2,
            combine_reports=True,
            report_name="api_test",
            report_title=f'sunbae App API Test', 
            template=template_path)
        
        runner.run(suite)
    except Exception as e:
        print(e)

 실행되는 중의 콘솔 화면

 

실행 후 html 파일 화면

4. Test Result Report 공유 

이렇게 완료가 된 레포트는 파일로 저장이 되는데, 이 결과를 여러 이해관계자들이 빠르게 볼 수 있도록,

주로 요즘은 Slack 을 많이 사용하는 편인 것 같아서 SlackAPI 를 통해서 공유하기로 했습니다.

slackAPI 로 메시지를 보내거나 파일을 업로드하는 것들은 생각보다 간단하기 때문에 여기에서 자세히 다루지는 않겠습니다.

* 다만 기존에 bot app에 권한이 files:wirte 권한이 없다면 추가를 해서 app을 재설치 해주어야 합니다 

# main.py
import unittest
import os
from sunbaeVerificateFile import sunbaeVerificate
import HtmlTestRunner
import glob


if __name__ == '__main__':

    try:
        sunbae_TestCase1 = unittest.TestLoader().loadTestsFromTestCase(sunbaeVerificate)
        suite = unittest.TestSuite(
                [sunbae_TestCase1])
        # 여러 개의 경우 [sunbae_TestCase1,sunbae_TestCase2]
        
        runner = HtmlTestRunner.HTMLTestRunner(
            output='api_results',
            verbosity=2,
            combine_reports=True,
            report_name="api_test",
            report_title=f'sunbae App API Test', 
            template=template_path)
        
        runner.run(suite)
    except Exception as e:
        print(e)
    finally:
        api_results_dir = os.path.join(parent_dir,'api_results','*')
        sorted_files = sorted(glob.iglob(api_results_dir), key=os.path.getctime, reverse=True) 
        pdfFileName = f'{os.path.splitext(sorted_files[0])[0]}.pdf'
        pdfkit.from_file(sorted_files[0], pdfFileName)
        slackapi_file(pdfFileName)

저 같은 경우에는 기존에 만들어둔 함수로 경로를 추가해서 보낼 수 있게 만들어둔 게 있어서 try except 문에 finally로 추가를 했습니다.

(여기에는 pdf로 되어있는 이유는 슬랙에 html을 공유하게되면 report를 보려면 파일을 다운로드 받고 저장 후 실행을 해야하는 번거로움이 있어서 html to pdf 라이브러리를 사용하여 공유를 했습니다.)

 

 

 

728x90
반응형