# 일주일동안 장고하기 3일차

## 복습 및 환경설정 정리

어제 하던 작업을 이어하던 도중 python 버전이 안 맞는 일이 발생했다. venv 환경이 python 2.7 로 변경되어 있었는데 원인은 모르겠다. 개발하다보면 불가사의한 일들이 발생하곤 한다.. 기존 장고 버전은 5.0 이었는데 이 버전은 파이썬 3.7 환경에서 실행될 수 없었다... 어제는 어떻게 실행된 건지 또한 알 수 없다. 아무튼 django 버전을 3.2 로 다운그레이드하고 파이썬 3.7 환경에서 다시 시작했다.

그리고 가상환경 관련 파일을 한번에 핸들링하기 어려워서 디렉토리를 프로젝트 내부에 포함시키게 되었다.

기존 디렉토리 구조는 다음과 같았다.

```plaintext
.
├── README.md
├── requirements.txt
├── django-site
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite
│   └── polls
├── bin
├── include
├── lib
└── pyvenv.cfg
```

venv 디렉토리를 삭제하기 위해 프로젝트 내부에 가상환경을 설정하게 되었다.

```plaintext
.
├── README.md
├── django-site
├── requirements.txt
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite
│   └── polls
└── venv
    ├── bin
    ├── include
    ├── lib
    └── pyvenv.cfg
```

바뀐 구조는 venv 환경을 삭제하고 다시 실행하기 유리하다는 장점이 있다.

새로 개발환경을 구성하니 (삭제하고 다시 clone 했다) admin 계정과 저장한 데이터들이 사라져있었다. 덕분에 데이터를 dump 하는 방법을 살펴볼 수 있었다. dump 파일을 git 에 올렸다가 admin 계정정보가 노출되면 어떡하나 고민했지만 비밀번호는 해싱된 채로 저장된다고 한다. 그래도 가급적이면 db 관련정보는 노출하면 안될 것이다. [data dump 하는 방법](https://docs.djangoproject.com/en/5.0/ref/django-admin/#dumpdata)

4장까지 진도를 다시 복습해보고 5장을 시작하게 되었다. 3, 4 장은 view, template 관련 문법이라 지루했지만 5장은 관심이 많은 테스트 코드 관련 문서였다.

## 5장 테스트 코드

테스트 코드의 중요성은 아무리 강조해도 지나치지 않는다. 코드의 작동이 엄밀해야 할수록 (작성된 코드가 돈을 벌어 올 수록) 테스트의 중요성은 더욱 커진다. 에러 케이스를 예방하는 데 도움을 주고 테스트를 자체가 요구사항을 의미하기 때문에 요구사항을 보장한다. 그리고 테스트 가능한 코드는 그 자체로 모듈화를 보장하고 잘 모듈화된 코드는 재사용성과 기능의 가독성을 보장한다. 테스트 코드를 만드는 것은 어렵지만, 어려운 만큼 큰 보상이 있다는 뜻이다. 귀찮아도 열심히 만들자.

테스트를 위해 `polls/`[`test.py`](http://test.py) 파일을 만들었다.

django.test 모듈에서 TestCase 라는 클래스를 불러와서 사용한다.

```python
from django.test import TestCase
```

여기서는 이전에 만들었던 `Question` 모델의 `was_published_recently()` 함수에 대한 테스트를 작성한다. `was_published_recently()` 함수는 해당 객체가 현재 날짜에서 1일 안에 작성되면 `True` 를 반환하는 함수이다. 여기서 작성하려는 테스트는 하루 이내에 작성된 객체는 `True` 를 반환 하는지, 1일 이전에 추가된 객체, 미래에 추가된 객체에 대해 `False` 를 반환 하는지 검사하는 코드이다.

```python
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)

    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() returns False for questions whose pub_date
        is older than 1 day.
        """
        time = timezone.now() - datetime.timedelta(days=1, seconds=1)
        old_question = Question(pub_date=time)
        self.assertIs(old_question.was_published_recently(), False)

    def test_was_published_recently_with_recent_question(self):
        """
        was_published_recently() returns True for questions whose pub_date
        is within the last day.
        """
        time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)
```

`QuestionModelTests` 라는 객체는 위에서 import 한 `TestCase` 클래스를 상속받아서 생성했다. 겸사겸사 python 상속에 대해서도 알아보면

```python
class Country:
	~~ 내용 ~~

class Korea(Country):
	~~ 내용 ~~
```

위와 같이 사용하면 상속이 된다

`TestCase` 객체를 상속받았기 때문에 `assertIs()` 같은 함수를 이용할 수 있다.

테스트 코드를 작성하고 다음과 같이 명령어를 입력하면 polls 앱 내부의 테스트를 자동으로 수행한다.

```bash
python manage.py test polls
```

제대로 테스트를 작성했다면 다음과 같이 출력된다.

```plaintext
(venv) ➜  django-site git:(main) ✗ python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

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

자동 실행되는 테스트 코드를 만들려면 몇 가지 사항을 유의해야 한다.

* 파일 이름이 test 로 시작해야 함
    
* 함수 이름이 test 로 시작해야 함
    

아래로는 view 를 테스트하는 부분이 있는데, 이 부분은 한번 해 보기만 하고 넘어갔다. `TestCase.client` 를 가지고 요청에 대한 response 를 테스트해보는 방법이었다. E2E 테스트의 일종인데 나중에 DRF 를 하며 더 상세하게 해보면 좋을 것 같다.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">그 다음에 나오는 테스트들을 따라할 때 <code>self.assertQuerySetEqual()</code> 함수가 나오는데 오타이다. <code>self.assertQuerysetEqual()</code> 로 수정해서 테스트하도록 하자.</div>
</div>

해당 오류를 제보하려고 했는데 오류 제보하는 방법이 너무 복잡해서 포기했다. 티켓을 발급해서 제보하는 방식인데, 제보한다고 해서 오픈소스에 기여되는 것도 아니고 시간과 정신을 쏟아서 굳이 제보할 이유를 찾지 못했다. 심지어 이전 버전에서 해당 에러에 대한 제보가 있었는데도 불구하고 변경되지 않은 오타이다.

```plaintext
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 10 tests in 0.034s

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

모든 테스트를 따라해보고 마무리했다.

6장은 app 의 static 디렉토리 내부에 css, image file 등 정적 파일을 추가하고 불러오는 내용이다. 스킵하고 넘어갔다.
