Model Controller Testsuite (MCT)을 이용한 테스트 자동화 구축

MVC는 아시겠지요. 그럼 MCT는요?

Model–view–controller(MVC)는 디자인 패턴으로, 프로그램 개발을 간소화하고 유지보수하기 쉽게 만드는 것이 주 목적입니다. 프로그램을 세 개의 논리적 모듈로 나누고 각 모듈에 구체적인 역할을 부여하는 방식인데, 이는 전송하는 데이터와 사용자에게 표출할 결과를 분리해 다루기 위함입니다. MVC 디자인 패턴을 적용해서 모듈을 분리하면 코드의 재사용성이 높아지고 역할 별로 요구 사항을 나누어 병렬적으로 개발을 진행할 수 있습니다.

QA 엔지니어 역시 MVC 디자인 패턴이 갖는 모듈화의 장점을 테스트 개발 아키텍처에 적용할 수 있습니다. 이 글에서는 MVC 디자인 패턴에서 영감을 받아 LINE TAIWAN에서 개발하여 활용하고 있는 테스트 자동화 아키텍처인 Model Controller Testsuite(MCT)를 설명하고, 이를 이용해 테스트 아키텍처를 더욱 안정적이고 유지보수하기 쉽게 만드는 방법을 알아보겠습니다.

MCT 디자인 패턴을 어떻게 적용해야 할까요?

앞서 말씀드린 것처럼 MCT를 사용해 테스트 아키텍처 프레임워크를 설계하고 개발하려면 어떻게 해야 할까요?
테스트 자동화를 위한 MCT 설계 구조는 주로 다음과 같은 세 모듈로 이루어집니다.

  • MODEL: 데이터, 리소스, 환경변수, 도구
  • CONTROLLER: 키워드, 라이브러리
  • TESTSUITE: 테스트 케이스

Model

Model 레이어의 기능은 MVC에서 정의한 대로 테스트에 필요한 데이터를 완전하게 제공하는 것입니다. 필요한 데이터란 변수 값, 필요한 실행 도구, 필요한 정적/동적 파일 같은 것들입니다. QA 엔지니어는 각각의 데이터 소스를 손쉽게 관리하고 테스트도 편리하게 진행할 수 있습니다.

Model 레이어는 데이터 읽기와 쓰기, 그리고 도구 사용만 담당합니다. 나라별, 테스트 환경별로 다른 요구 사항이나 사용자 정의 도구를 이용해 생성해야 하는 정보 등이 model을 통해 controller에게 제공되고, controller는 이 데이터를 사용하게 됩니다.

Controller

Controller는 model과 테스트 케이스 간의 인터랙션을 담당하는 중간 레이어입니다. 이 레이어는 테스트 케이스가 사용할 수 있도록 전체 테스트 아키텍처에 필요한 함수 라이브러리를 제공하며, model 레이어에서 얻은 데이터 셋 혹은 도구가 생성한 정보를 적용한 후 상응하는 결과를 테스트 케이스에 반환합니다. Controller 레이어에서는 널리 쓰이는 Page Object Model(POM) 디자인 패턴이나 자체 개발한 라이브러리를 모두 사용할 수 있습니다.

웹사이트 테스트 자동화 프로젝트를 진행하는 데 Selenium 웹드라이버를 사용한다고 가정해봅시다. 이때 controller는 QA 엔지니어가 개발하고 설계한 page object를 제공할 수 있고, 필요에 따라 직접 개발한 API가 들어있는 라이브러리를 제공할 수도 있습니다. Controller는 model 레이어와 관련한 모든 데이터를 처리하고, 테스트 케이스의 요구에 따라 그 결과를 반환합니다.

Testsuite

Testsuite는 테스트 현황 표출만을 담당합니다. 엔지니어이든 프로젝트 관련 매니저이든, 여기서는 테스트 규격이나 규격을 변환한 테스트 케이스를 제공하기만 하면 됩니다. 테스트 결과가 예상한 것과 일치하는지만 알면 되고, 테스트 로직을 어떻게 코드로 구현하는지는 신경 쓰지 않아도 됩니다.

MCT 사용 예

LINE TODAY 로그인을 예로 들어봅시다. 이 테스트 케이스는 LINE TODAY 로그인 성공 여부를 보여줍니다. 아래 그림에서 보는 것처럼 로그인이 성공한 후에는 개인 프로필 사진이 정확하게 나타납니다.

LINE TODAY Not logged in After logging into LINE TODAY

이 테스트 케이스를 만드는 데 필요한 설계 구조는 다음과 같습니다.

Model

Model 레이어는 테스트에 필요한 사용자 정보와 관련 변수 값, 즉 URL, 국가, 뉴스 분류 같은 것을 제공합니다.
LINE TODAY의 예에서는 다음과 같은 데이터가 model에 들어갑니다.

global_vars = {
    "USERNAME": "user",
    "PASSWORD": "passw0Rd",
    ...
}
tw_variables = {
    "COUNTRY": "TW",
    "TODAY": "https://today.line.me/tw"
    "CATEGORIES": [u"국내", u"오락", u"생활", u"네티즌", u"국제"],
    ... 
},
id_variables = {
    "COUNTRY": "ID",
    "TODAY": "https://today.line.me/id"
    "CATEGORIES": ["News", "Showbiz", "Sports", "Life"],
    ...
}

Controller

Controller에는 테스트 케이스가 쉽게 사용할 수 있는 여러가지 라이브러리를 제공합니다. 아래 코드에서도 page object model 디자인 패턴이 적용된 것을 볼 수 있습니다. 덕분에 개발자들은 웹사이트 설계 로직에 집중할 수 있습니다.

from selenium import webdriver
class LoginPage(self):
    browser = webdriver.Chrome()
    def click_login_entrance(self):
        """Click the login entrance"""
        browser.click_button(self.locator.login_entrance_button) 
    def enter_username(self, username):
        """Enter the given string into the username field"""
        browser.input_text(self.locator.username, username)
    def enter_password(self,password):
        """Enter the given string into the password field"""
        browser.input_text(self.locator.password, password)
    def click_the_submit_button(self):
        """Click the submit button, and wait for the page to reload"""
        browser.click_button(self.locator.submit_button)
    ...

물론 QA 엔지니어도 테스트 요구 사항에 따라 사용자 정의 라이브러리를 개발할 수 있습니다. 프로젝트에서 사용할 라이브러리를 개발하는 가장 중요한 목적은 빌트인 라이브러리의 부족한 부분을 보완하는 것입니다. 예를 들어, 포스팅한 글에 “좋아요”를 누르는 기능을 생각해봅시다. 빌트인 라이브러리는 우리가 원하는 대로 “좋아요” 버튼을 누르는 방법을 다양하게 제공하지 않습니다. 이럴 때는 아래 코드처럼 controller에 `add_liked_to_article`을 구현하면 됩니다.

import requests
class SampleCodeAPI:
    def __init__(self):
        ...
        .........
        ......
    def add_liked_to_article(self, article_id, like_type, cookies):
        api_url = '{api_url}/like/add'.format(api_url=self.api_url)
        payload = {
            "article": article,
            "country": country,
            "likeType": like_type
         }
        res = requests.post(api_url, headers={"Cookie": cookies}, json=payload)
        try:
            return res.json()
        except:
            ResponseError("Response error")

Testsuite

Testsuite 레이어에서는 보시다시피 규격 케이스나 테스트 케이스를 쓰기만 하면 됩니다. QA 엔지니어가 테스트 케이스를 적용하고 타겟 비지니스 로직을 검증하는 데 더욱 집중할 수 있다는 뜻입니다. 이런 방식이라면 기술적 배경이 없는 프로젝트 멤버도 비지니스 로직 스펙을 쓸 수 있기 때문에 누구나 테스트 과정에 참여할 수 있게 됩니다.

*** Test Cases ***
    Given User go to LINE TODAY 
    When Login with valid credentials
    Then Login as a normal user

LINE TAIWAN에서는 BDD(Behavior-driven development) 개발 방식을 채택하고 있습니다. BDD 방식은 소프트웨어 프로젝트에서 개발자, QA 엔지니어, 비기술자 및 비지니스 파트너가 원활하게 소통하고 제품과 스펙에 대해 명확한 컨센서스를 이루게 해줍니다.

맺음말

테스트 자동화 프레임워크에 계층 구조의 MCT 디자인 패턴을 갖추면 테스트 환경이나 기본 아키텍처에 변화가 있을 때 개발자가 유연성을 확보하기 쉽습니다. 단순히 유지보수만 쉬워지는 것이 아니라 개발 속도도 좀 더 빨라집니다.

일반적으로 모듈 간 결합도가 너무 높으면 하드코딩된 테스트 코드나 데이터가 대량으로 들어가므로 변경 비용이 높아집니다. 이 글에서 소개했듯이 MCT 디자인 패턴을 적용하면 유지보수 비용이 낮아지고 코드의 로직이 분명해지며 테스트 케이스도 명확해지는 효과를 얻을 수 있습니다.