본문 바로가기
Web/Django, Bootstrap

[Blog 만들기] #7 테스트 주도 개발(TDD; Test driven development)

by 파크영 2022. 6. 19.

※ Do it! 장고 + 부트스트랩 파이썬 웹 개발의 정석(이성용, 김태곤 / 이지스퍼블리싱) 책을 참고하여 프로젝트를 진행하고 있습니다.

프로젝트 진행과정 기록하기 위해 작성하고 있으므로 책 저작권으로 인해 간단한 내용 외 자세한 코드와 내용은 작성하지 않습니다.

 

 

테스트 주도 개발(TDD; Test driven development)

일종의 개발 방식 또는 개발 패턴

개발할 때 바로 개발부터 하는 것이 아니라 개발하려는 항목에 대한 점검 사항을 테스트 코드로 만들고 그 테스트를 통과시키는 방식으로 개발을 진행하는 방법

 

테스트 주도 개발 적용 하지 않을 때의 과정

구현 -> 직접 확인(웹이나 앱으로 들어가서 잘 작동하는지 일일히 TEST) -> 성공(실패시 다시 수정하여 성공시킴) -> 개선(다시 개발해야할 부분이나 개선시킬 방법을 생각) -> 구현(다시 1번으로 돌아감)

 

위의 과정을 반복하며 개발하게 된다. 

단순한 프로그램을 개발한다면 이 방법이 편하겠지만 조금 더 복잡한 프로그램을 개발하게 된다면 이 방법은 비효율적일 수 있다.

따라서 개발한 코드가 테스트를 만족하는지 자동으로 확인하면서 개발을 진행하게 된다면 매번 직접 테스트하지 않고 사고가 발생할 확률도 줄어들 것이다. 

 

 

※ 테스트 주도 개발 과정

 

테스트 코드 작성 -> 기능 구현 -> 리팩토링

 

테스트 코드 작성 
- 만들고 싶은 기능을 점검할 코드 작성

기능 구현 
- 테스트 코드를 만족시킬 수 있게 기능 구현
- 테스트 통과를 최우선으로 생각하고 작업

리팩토링 
- 기능의 성능을 향상시키거나, 재사용성이 좋거나, 가독성이 좋은 코드로 개선
- 테스트 코드로 다시 기능 점검

 

 

 


블로그 포스트 목록 페이지와 상제 페이지 테스트 

 

blog/tests.py

from django.test import TestCase, Client
from bs4 import BeautifulSoup
from django.contrib.auth.models import User
from .models import Post

# Testcase를 이용한 테스트 방식은 가상의 DB를 새로 만들어 테스트한다.

# Create your tests here.

class TestView(TestCase):
    def setUp(self):
        self.client = Client()
        self.user_trump = User.objects.create_user(username='trump', password='somepassword')
        self.user_obama = User.objects.create_user(username='obama', password='somepassword')

    def navbar_test(self, soup):
        navbar = soup.nav
        self.assertIn('Blog', navbar.text)
        self.assertIn('About Me', navbar.text)

        # navbar에서 경로로 잘 이동하는지 점검하기
        logo_btn = navbar.find('a', text='Do It Django')
        self.assertEqual(logo_btn.attrs['href'], '/')

        home_btn = navbar.find('a', text='Home')
        self.assertEqual(home_btn.attrs['href'], '/')

        blog_btn = navbar.find('a', text='Blog')
        self.assertEqual(blog_btn.attrs['href'], '/blog/')

        about_me_btn = navbar.find('a', text='About Me')
        self.assertEqual(about_me_btn.attrs['href'], '/about_me/')

    def test_post_list(self):
        # 1.1 포스트 목록 페이지 가져오기
        response = self.client.get('/blog/')
        # 1.2 정상적으로 페이지가 로드된다.
        self.assertEqual(response.status_code, 200)
        # 1.3 페이지 타이틀은 'Blog'이다.
        soup = BeautifulSoup(response.content, 'html.parser')
        self.assertEqual(soup.title.text, 'Blog')
        # 1.4 내비게이션 바가 있다.
        self.navbar_test(soup)

        # 2.1 메인 영역에 게시물이 하나도 없다면
        self.assertEqual(Post.objects.count(), 0)
        # 2.2 '아직 게시물이 없습니다'라는 문구가 보인다.
        main_area = soup.find('div', id='main-area')
        self.assertIn('아직 게시물이 없습니다', main_area.text)

        # 3.1 게시물이 2개 있다면
        post_001 = Post.objects.create(
            title='첫 번째 포스트입니다.',
            content='Hello World. We are the world',
            author=self.user_trump
        )
        post_002 = Post.objects.create(
            title='두 번째 포스트입니다.',
            content='1등이 전부는 아니잖아요?',
            author=self.user_obama
        )
        self.assertEqual(Post.objects.count(), 2)

        # 3.2 포스트 목록 페이지를 새로고침했을 때
        response = self.client.get('/blog/')
        soup = BeautifulSoup(response.content, 'html.parser')
        self.assertEqual(response.status_code, 200)
        # 3.3 메인 영역에 포스트 2개의 타이틀이 존재한다.
        main_area = soup.find('div', id='main-area')
        self.assertIn(post_001.title, main_area.text)
        self.assertIn(post_002.title, main_area.text)
        # 3.4 '아직 게시물이 없습니다'라는 문구는 더 이상 보이지 않는다.
        self.assertNotIn('아직 게시물이 없습니다', main_area.text)

        self.assertIn(self.user_trump.username.upper(), main_area.text)
        self.assertIn(self.user_obama.username.upper(), main_area.text)


    def test_post_detail(self):
        # 1.1 포스트가 하나 있다.
        post_001 = Post.objects.create(
            title='첫 번째 포스트입니다.',
            content='Hello World. We are the world',
            author=self.user_trump
        )
        # 1.2 그 포스트의 url은 '/blog/1/'이다.
        self.assertEqual(post_001.get_absolute_url(), '/blog/1/')

        # 2. 첫 번째 포스트의 상세 페이지 테스트
        # 2.1 첫 번째 포스트의 url로 접근하면 정상적으로 작동한다.(status code:200)
        response = self.client.get(post_001.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        soup = BeautifulSoup(response.content, 'html.parser')

        # 2.2 포스트 목록 페이지와 똑같은 내비게이션 바가 있다.
        self.navbar_test(soup)

        # 2.3 첫 번째 포스트의 제목이 웹 브라우저 탭 타이트에 있다.
        self.assertIn(post_001.title, soup.title.text)

        # 2.4 첫 번째 포스트의 제목이 포스트 영역에 있다.
        main_area = soup.find('div', id='main-area')
        post_area = main_area.find('div', id='post-area')
        self.assertIn(post_001.title, post_area.text)

        # 2.5 첫 번째 포스트의 작성자(author)가 포스트 영역에 있다.
        self.assertIn(self.user_trump.username.upper(), post_area.text)

        # 2.6 첫 번째 포스트의 내용(content)이 포스트 영역에 있다.
        self.assertIn(post_001.content, post_area.text)

 

TestCase를 사용한 테스트 방식은 실제 DB를 사용하지 않고 운영되고 있는 서버의 데이터베이스를 건드리지 않기 위해 가상의 데이터베이스를 새로 생성하여 서버를 돌리며 테스트한다.  

댓글