---
description: 210729, 210730
---

# TDD (Test Driven Development)

## TDD란 무엇인가?

### 테스트 주도 개발

#### 기존 개발 방식

1. 구현
2. 웹 브라우저로 직접 확인
3. 성공
4. 개선점 찾기

#### TDD

1. 테스트 코드 작성
2. 기능 구현
3. 리팩토링

#### TDD를 하는 이유

* 프로그램이 복잡해 질수록, 기능을 추가할 때마다 기능 사이에 연광성이 커진다
* 새로운 기능이 추가될 때 마다 브라우저에서 확인이 어려워진다
* 문제가 발생했는지 모르고 개발을 하다가, 추후에 수정할 때 건드리기 어려움



## 블로그 목록 페이지에 대한 테스트 코드 만들기

```python
from django.test import TestCase

# Create your tests here.
class TestView(TestCase):
    def test_post_list(self):
        self.assertEqual(2, 2)

        # 1.1 포스트 목록 페이지(post list)를 연다
        # 1.2 정상적으로 페이지가 로드된다.
        # 1.3 페이지의 타이틀에 Blog라는 문구가 있다.
        # 1.4 NavBar가 있다
        # 1.5 Blog, About me라는 문구가 Nav에 있다.

        # 2.1 게시물이 하나도 없을 때
        # 2.2 메인 영역에 "아직 게시물이 없습니다" 라는 문구가 나온다.

        # 3.1 만약 게시물이 2개 있따면,
        # 3.2 포스트 목록 페이지를 새로 고침했을 때,
        # 3.3 메인 영역에 포스트 2개의 타이틀이 존재한다
        # 3.4 "아직 게시물이 없습니다" 라는 문구가 없어야 한다다
```

* 시작은 이렇게 말로 다 써놓는다.
* 혼자서 개발하는게 아니라 여럿이 개발할 때 이러한 과정이 도움이 된다.



`test.py`

```python
from django.test import TestCase, Client
from bs4 import BeautifulSoup
from .models import Post


class TestView(TestCase):
    def setUp(self):
        self.client = Client()

    def test_post_list(self):
        pass
        # 1.1 포스트 목록 페이지(post list)를 연다
        response = self.client.get('/blog/')
        # 1.2 정상적으로 페이지가 로드된다.
        self.assertEqual(response.status_code, 200)
        # 1.3 페이지의 타이틀에 Blog라는 문구가 있다.
        soup = BeautifulSoup(response.content, 'html.parser')
        self.assertIn('Blog', soup.title.text)
        # # 1.4 NavBar가 있다
        navbar = soup.nav
        # # 1.5 Blog, About me라는 문구가 Nav에 있다.
        self.assertIn('Blog', navbar.text)
        self.assertIn('About me', navbar.text)

        # 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.',
        )

        post_002 = Post.objects.create(
            title='두번째 포스트 입니다.',
            content='안녕 여러분, 나도 여러분의 일부야.',
        )
        self.assertEqual(Post.objects.count(), 2)

        # 3.2 포스트 목록 페이지를 새로 고침했을 때,
        response = self.client.get('/blog/')
        soup = BeautifulSoup(response.content, 'html.parser')
        # 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)
```

* assertEqual(A, B)
  * A와 B가 같아야 한다. 같지 않으면 python manage.py test 에서 오류가 난다.
  * 위에서는 soup.title.text와 'Blog'가 같아야 한다로 코드를 작성하면서 하나씩 테스트 하고 있다
* assertIn\[assertNotIn]\(A, B)
  * A가 B안에 포함되\[지 않아]어야 한다라는 코드

`post_list.html`

```markup
            <div class="col-md-8 col-lg-9" id="main-area">
                {% if post_list.exists %}
                    {% for p in post_list %}
                    <!-- Blog post-->
                    <div class="card mb-4">
                        {% if p.head_image %}
                            <img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p.title }}" />
                        {% else %}
                            <img class="card-img-top" src="https://picsum.photos/seed/{{ p.id }}/700/400" alt="{{ p.title }}">
                        {% endif %}
                        <div class="card-body">
                            <div class="small text-muted">January 1, 2021</div>
                            <h2 class="card-title h4">{{ p.title }}</h2>
                            {% if p.hook_text %}
                                <h5 class="text-muted"> {{ p.hook_text }} </h5>
                            {% endif %}
                            <p class="card-text">{{ p.content | truncatewords:50 }}</p>
                            <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
                        </div>
                        <div class="card-footer text-muted">
                            Posted on {{ p.created_at }} Update at {{ p.updated_at }} by
                            <a href="#">작성자명 쓸 위치</a>
                        </div>
                    </div>
                    {% endfor %}
                {% else %}
                    <h1>아직 게시물이 없습니다</h1>
                {% endif $}
```

* div에 `main-area` 라는 id를 추가했다
* `post_list.exists` 로 post\_list가 존재하는지에 대한 if문을 추가했다



## 블로그 상세 페이지에 대한 테스트 코드 만들기

`tests.py`

```python
 def test_post_detail(self):
        # 1.1 포스트가 하나 있다
        # 1.2 그 포스트의 url은 `/blog/1/` 이다
        # 2. 첫번째 포스트의 상세 페이지 테스트
        # 2.1 첫번째 포스트의 url로 접근하면 정상적으로 response가 온다(status code : 200)
        # 2.2 포스트 목록 페이지와 똑같은 내비게이션 바가 있다.
        # 2.3 첫번째 포스트의 제목이 웹 브라우저 탭 타이틀에 들어있다.
        # 2.4 첫번째 포스트의 제목이 포스트 영역에 있다.
        # 2.5 첫번째 포스트의 작성자(author)가 포스트 영역에 있다(아직 구현할 수 없음)
        # 2.6 첫번째 포스트의 내용(content)이 포스트 영역에 있다.
```

```python
def test_post_detail(self):
    # 1.1 포스트가 하나 있다
    post_001 = Post.objects.create(
        title='첫번째 포스트 입니다.',
        content='Hello, World. We are the World.',
    )
    self.assertEqual(Post.objects.count(), 1)
    # 1.2 그 포스트의 url은 `/blog/1/` 이다
    self.assertEqual(post_001.get_absolute_url(), '/blog/1')
    # 2. 첫번째 포스트의 상세 페이지 테스트
    # 2.1 첫번째 포스트의 url로 접근하면 정상적으로 response가 온다(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 포스트 목록 페이지와 똑같은 내비게이션 바가 있다.
    navbar = soup.nav
    self.assertIn('Blog', navbar.text)
    self.assertIn('About me', navbar.text)
    # 2.3 첫번째 포스트의 제목이 웹 브라우저 탭 타이틀에 들어있다.
    self.assertIn(post_001.title, soup.title)
    # 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)가 포스트 영역에 있다(아직 구현할 수 없음)
    # 2.6 첫번째 포스트의 내용(content)이 포스트 영역에 있다.
    self.assertIn(post_001.content, post_area.text)
```



