TITEDIOS 편한 코딩

[Django] 테스트 자동화하기 - 투표 앱 만들기(Django tutorial) 본문

Django

[Django] 테스트 자동화하기 - 투표 앱 만들기(Django tutorial)

TitediosKW 2024. 11. 19. 19:00
반응형

목차

  1. Django 모델 코드 수정하기
  2. 테스트 코드 작성하기
  3. 버그(Bug) 수정하기
  4. 테스트에 관한 사실
  5. 결론

Django 테스트는 Python의 내장 unittest 모듈을 기반으로 하며, 빠르게 설정하고 확장할 수 있는 기능들을 지원합니다. 이번 실습에서는 Django 테스트의 사례를 통해 테스트를 자동화하는 방법을 단계별로 살펴보며, 안정적인 코드베이스를 구축하는 방법에 대해 실습해 보겠습니다. 코딩은 손가락으로 하는 것입니다. 눈으로만 봐도 좋겠지만 직접 키보드를 두드리면서 느끼는 개발자만의 느낌을 느껴보시길 바라겠습니다.


1. Django 모델 코드 수정하기

우선 Django 모델 코드를 수정해 보겠습니다. 'polls/models.py` 파일에서 Question과 Choice를 수정합니다.

from django.db import models


class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text
  • __str__() 함수는 객체 자체를 출력할 때 넘겨주는 형식을 지정해 주는 메서드입니다. 그러니까 Question의 객체를 출력하면 전달되는 문자열을 의미합니다.
  • was_published_recently() 함수는 우리가 직접 구현한 코드입니다. 이 메서드는 단순하게 해당 Question 객체가 생성된 지 하루가 넘지 않았다면 True 아니라면 False를 반환합니다.
반응형

2. 테스트 코드 작성하기

Django 모델 중 Question 모델에 was_published_recently() 함수를 구현했습니다. 그런데, 이 코드 정말로 우리가 원하는 대로 동작하는 게 맞을까요? 지금은 단순하게 한 줄짜리 코드일 뿐이지만 1,000 줄이 넘어가는 복잡한 코드라면 어떨까요? 직접 눈으로 검사하는 것에는 분명히 한계가 있을 것입니다. 그래서, 우리는 테스트 코드를 작성해야 합니다.

 

테스트 코드를 작성해 보겠습니다. polls/tests.py을 수정해 봅시다.

import datetime

from django.test import TestCase
from django.utils import timezone

from .models import Question


class QuestionModelTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() returns False for questions whose pub_date
        is in the future.
        """
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)

 

테스트 코드에 대해 아래에서 설명하겠습니다.

  • timezone.now(): 현재 시간 반환.
  • datetime.timedelta(days=30): 현재 시간에서 30일을 더한 시각을 생성. 이는 "미래의 날짜"를 표현합니다.
  • future_question = Question(pub_date=time): pub_date가 미래 날짜인 Question 객체를 생성합니다. 데이터베이스에 저장하지 않아도 이 상태로 테스트 가능합니다.
  • assertIs: self.assertIs(a, b)는 a와 b가 같은 객체인지 검증하는 테스트 도구입니다. was_published_recently() 메서드를 호출하여 반환값이 False인지 확인합니다.
  • 예상되는 동작: pub_date가 미래에 있으므로 was_published_recently()는 False를 반환해야 합니다.

was_published_recently 메서드는 "최근 게시 여부"를 판단하는 기능을 수행하지만, 우리가 구현한 코드는 미래 날짜에 대한 제약이 없습니다. 이 테스트는 메서드가 미래 날짜에 대해서도 False를 반환하도록 기능의 완전성을 보장하기 위한 것입니다.

예상되는 테스트 시나리오

  • 성공: was_published_recently가 미래 날짜에 대해 False를 반환하면, 테스트는 통과합니다.
  • 실패: 만약 메서드가 True를 반환하거나 예외를 발생시키면 테스트는 실패로 기록됩니다.

python manage.py test

 

위의 명령으로 테스트를 실행해 보겠습니다. 명령으로 작성한 테스트 케이스의 실행이 가능하며 테스트를 하면 위와 같은 화면일 것입니다. 화면에서 F 가 보이시죠? 이 F는 1개만 보이기 때문에 실패 즉, Fail인 테스트 케이스가 1개 있다는 의미입니다. 그리고 아래애 AssertionError 메시지가 보입니다. 테스트가 실패한 이유에 대해 말해주죠. False를 기대했지만 True가 반환되었다는 메시지입니다.


3. 버그(Bug) 수정하기

테스트를 통해 버그가 발견되었습니다. 우리가 코드를 구현할 때 미래의 시간에 대한 고려를 하지 못했다는 사실이 밝혀졌습니다. 이제 미래의 시간에 대해서도 고려하여 코드를 수정해 보겠습니다.

polls/models.py

import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    # ...
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now

 

조건문은 다음 두 가지를 동시에 만족해야 True를 반환합니다:

  1) self.pub_date가 "현재 시간에서 1일 전" 이후여야 합니다. (즉, self.pub_date >= now - datetime.timedelta(days=1))
  2) self.pub_date가 "현재 시간"보다 같거나 이전이어야 합니다. (즉, self.pub_date <= now)

 

코드를 수정했으니까 우리의 의도대로 동작하는지 테스트를 수행합니다. 이제 'OK' 메시지를 확인하실 수 있습니다. 테스트가 성공한 것을 보니 우리의 코드가 잘 동작하는 것 같습니다.


4. 테스트에 관한 사실

여기서부터는 테스트에 대한 줄 글입니다. 긴 글이 줄줄줄 나오는다는 얘기입니다. 그저 테스트가 좋다, 필요하다는 이야기를 길게 나열한 글일 뿐이겠지만 시간이 있으시다면 읽어보시는 것도 도움이 되겠습니다.

1) 자동화된 테스트란?

테스트는 코드 작동을 확인하는 루틴입니다.

 

테스트는 여러 수준에서 작동합니다. 일부 테스트는 사소한 세부 사항에 적용되는 반면, 다른 테스트는 소프트웨어의 전반적인 작동을 검사합니다. 자동화된 테스트의 차이점은 테스트 작업이 "시스템에서 수행"된다는 것입니다. 한 번 테스트 세트를 만든 다음 앱을 변경하면서 시간이 많이 걸리는 수동 테스트를 수행하지 않고도 코드가 원래 의도한 대로 작동하는지 확인할 수 있습니다.

2) 테스트를 만들어야 하는 이유

그렇다면 왜 테스트를 만들고 지금 만들어야 할까요?

 

Python/Django를 배우는 것만으로도 충분히 할 일이 있다고 생각할 수 있고, 또 다른 것을 배우고 해야 한다는 것은 어렵고 불필요할 수 있습니다. 결국, 우리가 만든 애플리케이션은 지금 잘 작동하고 있습니다. 또, 자동화된 테스트를 만드는 수고스러움이 애플리케이션을 더 잘 작동하게 만드는 것은 아닙니다. 애플리케이션을 만드는 것이 Django 프로그래밍의 마지막 부분이라면, 자동화된 테스트는 필요 없습니다. 하지만, 그렇지 않고 계속 개발 중이라면 테스트의 자동화는 필요합니다.

① 테스트는 시간을 절약해 줍니다

어느 정도까지는 '작동하는 것 같은지 확인하는 것'이 만족스러운 테스트가 될 것입니다. 하지만, 정교한 애플리케이션에서는 구성 요소 간에 수십 개의 복잡한 상호 작용이 있을 수 있습니다. 이러한 구성 요소 중 하나라도 변경하면 애플리케이션 동작에 예상치 못한 결과가 발생할 수 있습니다. 여전히 '작동하는 것 같은지' 확인하려면 테스트 데이터의 20가지 변형으로 코드의 기능을 실행하여 무언가를 망가뜨리지 않았는지 확인해야 할 수 있습니다. 시간 낭비입니다.

 

특히 자동화된 테스트가 몇 초 만에 이 작업을 대신 수행할 수 있는 경우에는 더욱 그렇습니다. 무언가 잘못되었을 경우 테스트는 예상치 못한 동작을 일으키는 코드를 식별하는 데에도 도움이 됩니다.

 

때로는 생산적이고 창의적인 프로그래밍 작업에서 벗어나 매력적이지 않고 흥미롭지 않은 테스트를 작성하는 일에 직면하는 것이 지루하게 느껴질 수 있습니다. 특히 코드가 제대로 작동한다는 것을 알고 있을 때 더욱 그렇습니다. 그러나 테스트를 작성하는 작업은 수동으로 애플리케이션을 테스트하는 데 몇 시간을 보내거나 새로 발생한 문제의 원인을 파악하려고 하는 것보다는 훨씬 더 생산적인 작업이 될 수 있습니다.

② 테스트는 문제를 예방합니다.

테스트를 단순히 개발의 부정적인 측면으로 생각하는 것은 실수입니다.

 

테스트가 없다면 애플리케이션의 목적이나 의도된 동작은 다소 불분명할 수 있습니다. 자신의 코드일지라도 때로는 코드가 정확히 무엇을 하는지 알아내려고 코드를 하나하나 살펴야 할 것입니다(실제로 하루이틀만 지나도 자신이 뭘 만들었는지 까먹을 때가 많습니다).

 

테스트는 이런 상황을 피하게 해 줍니다. 코드를 더 잘 이해할 수 있게 하고 문제가 발생하면 잘못된 부분에 초점을 맞춥니다. 심지어 문제가 발생했다는 것을 알아차리지 못했더라도 말입니다.

③ 테스트는 코드를 더 매력적으로 만듭니다.

훌륭한 소프트웨어를 만들었을지 몰라도, 테스트가 없으면 신뢰하지 않을 것입니다. Django의 최초 개발자 중 한 명인 Jacob Kaplan-Moss는 "테스트가 없는 코드는 설계상 망가졌습니다."라고 말합니다.

 

다른 개발자가 우리 코드를 진지하게 받아들이기 전에 소프트웨어에서 테스트를 보고 싶어 한다는 것은 테스트를 작성하기 시작해야 하는 또 다른 이유입니다.

④ 테스트는 팀의 협업을 돕습니다.

이전 요점은 애플리케이션을 유지 관리하는 단일 개발자의 관점에서 작성되었습니다. 하지만, 복잡한 애플리케이션은 에서 유지 관리합니다. 테스트는 동료가 실수로 코드를 망가뜨리지 않도록(그리고 자신도 모르게 그들의 코드를 망가뜨리지 않도록) 보장합니다. 프로그래머로 먹고사는 컴퓨터쟁이가 되고 싶다면 그리고 그 일을 좀 더 멋있게 하려면 테스트를 잘 작성해야 합니다.

3) 기본 테스트 전략

테스트를 작성하는 방법에는 여러 가지가 있습니다.

 

일부 프로그래머는 "테스트 주도 개발(TDD;Test-Driven Development)"이라는 원칙을 따릅니다. 실제로 코드를 작성하기 전에 테스트를 작성합니다. 반직관적으로 보일 수 있지만 사실 대부분 사람들이 어차피 자주 하는 일과 비슷합니다. 문제를 설명한 다음, 문제를 해결하기 위한 코드를 만듭니다. 테스트 주도 개발은 Python 테스트 사례에서 문제를 공식화합니다.

 

테스트를 처음 접하는 사람은 코드를 만든 다음 나중에 테스트가 필요하다고 결정하는 경우가 더 많습니다. 테스트를 일찍 작성하는 것이 더 나았을 수도 있지만 시작하기에 늦은 때는 없습니다.

 

테스트 작성을 어디서부터 시작해야 할지 파악하기 어려울 때가 있습니다. Python으로 수천 줄을 작성했다면 테스트할 항목을 선택하는 것이 쉽지 않을 수 있습니다. 그런 경우 다음에 변경 사항을 적용할 때, 즉 새 기능을 추가하거나 버그를 수정할 때 첫 번째 테스트를 작성하는 것이 좋습니다.


결론

테스트는 코드의 신뢰성과 안정성을 보장하기 위한 필수 도구로, 예상치 못한 오류를 사전에 예방하고 문제 발생 시 원인을 빠르게 파악할 수 있도록 합니다. 자동화된 테스트는 복잡한 애플리케이션에서 반복 작업을 줄여주며, 개발자 간 협업을 원활하게 하고 코드의 품질을 높이는 데 기여합니다. 시작 시점이 언제든 중요하지 않으며, 새로운 기능 추가나 버그 수정 시 테스트를 작성하는 것이 좋은 출발점입니다. 테스트는 단순한 확인 작업을 넘어, 코드의 의도를 명확히 하고 장기적으로 시간을 절약하는 생산적인 개발의 핵심입니다. 그리고, 좀 더 수준 높고 멋진 개발자가 되도록 하기 때문에 테스트를 좀 더 신경 쓰시면 좋겠습니다.

 

조금 더 Django와 친해지는 시간이 되셨길 바라면서 도움이 되셨다면 공감 부탁드리겠습니다. 여러분의 공감이 정말 큰 힘이 됩니다.

 

감사합니다!

반응형