# Django Automated Testing

자동화된 테스트는 앞서서 `shell` 을 사용해 메소드의 동작을 검사하거나 데이터를 입력해서 테스트 한것과 다르지 않다. 차이점은 테스트 작업이 시스템에서 수행된다는 점이다. 한번 테스트 세트를 작성한 후에는 앱을 변경할 때 수동 테스트를 수행하지 않아도 원래 의도대로 코드가 작동하는지 확인할 수 있다.

* 자동화된 테스트를 통해 시간을 절약할 수 있다. 테스트를 작성하는 작업은 어플리케이션을 **수동으로 테스트하거나 새로 발견된 문제의 원인을 확인하는 데 많은 시간을 투자하는 것보다 훨씬 더 효과적**입니다.
* 문제를 식별하는 것이 아니라 예방할 수 있다.

애플리케이션 테스트는 일반적으로 `<app_name>/tests.py` 파일에 있다. 테스트 시스템은 **test**로 시작하는 메소드를 자동으로 찾는다.

## Model Test

```python
# polls/models.py

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return '%s >> %s' % (self.question_text, self.pub_date)
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
```

```python
# polls/test.py
import datetime

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

from .models import Question

# Create your tests here.
class QuestionMdoelTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)

self.assertIs(future_question.was_published_recently(),False)
```

다음은 간단한 예시이다. 지금 시간보다 30일 이후의 Question을 생성한 뒤 최근에 생성한게 맞는지 확인하는 것이다.

```bash
$ python manage.py test <app_name>
```

```bash
$ python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMdoelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/jeongdaye/Documents/study/test_app/mysite2/polls/tests.py", line 13, in test_was_published_recently_with_future_question
    self.assertIs(future_question.was_published_recently(),False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
Destroying test database for alias 'default'...
```

우리는 False가 return되기를 바라는데 True가 반환된다는 것을 발견할 수 있다.

```python
# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return '%s >> %s' % (self.question_text, self.pub_date)
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
```

다음과 같이 `was_published_recently()` 를 수정해주고 테스트 명령어를 실행해보면 테스트를 통과한 것을 확인할 수 있다.

```bash
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database for alias 'default'...
```

## View Test

### Shell에 테스트 환경 구성하기

```bash
$ python manage.py shell
Python 3.7.2 (default, Mar  5 2019, 16:08:31) 
[Clang 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
```

```python
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
```

이 메소드는 테스트 데이터베이스를 설정하지 않기 때문에 현재 사용중인 데이터베이스 위에서 돌게되며 결과는 데이터베이스의 데이터에 따라 다르게 나온다.

```python
>>> from django.test import Client
>>> client = Client()
```

테스트 클라이언트를 생성하여 작업을 수행할 수 있다.

```python
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
>>> from django.test import Client
>>> client = Client()
>>> from django.urls import reverse
>>> response = client.get(reverse('polls:index'))
>>> response.status_code
200
>>> response.content
b'\n    <ul>\n    \n        <li><a href="/polls/2/">Are you happy?</a></li>\n    \n        <li><a href="/polls/1/">What&#39;s your name?</a></li>\n    \n    </ul>\n'
>>> response.context['latest_question_list']
<QuerySet [<Question: Are you happy? >> 2019-04-05 05:54:39>, <Question: What's your name? >> 2019-04-04 06:21:55.323284>]>
>>>
```

```python
def create_question(question_text, days):
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text, pub_date=time)


class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_past_question(self):
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_future_question(self):
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_future_question_and_past_question(self):
        create_question(question_text="Past question.", days=-30)
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_two_past_questions(self):
        create_question(question_text="Past question 1.", days=-30)
        create_question(question_text="Past question 2.", days=-5)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question 2.>', '<Question: Past question 1.>']
        )

class QuestionDetailViewTests(TestCase):
    def test_future_question(self):
        future_question = create_question(question_text='Future question.', days=5)
        url = reverse('polls:detail', args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        past_question = create_question(question_text='Past Question.', days=-5)
        url = reverse('polls:detail', args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)
```

다음과 같이 View에 대해서도 테스트 코드를 작성할 수 있다. 테스트를 할 때는 많이 할 수록 좋다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dahye-jeong.gitbook.io/til/django/advanced/2019-04-08-test.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
