class PhotoTest(TestCase): def setUp(self): """본 테스트에 공통으로 사용하는 데이터들. test users를 만들고 이 이용자가 접근한다는 전제로 사용함. """ self.client = Client() self.users = ( {'username': '******', 'password': '******'}, {'username': '******', 'password': '******'}, ) for _user in self.users: User.objects.create_user(**_user) self.urls = namedtuple('URL', ( 'create_photo', 'delete_photo', 'view_photo', 'list_photos', 'create_comment', 'delete_comment', ))( # 게시물 작성하는 URL name 은 'create_photo' lambda : reverse('photos:create_photo'), # 개별 게시물 지우는 URL name 은 'delete_photo'이며, # URL패턴의 그룹은 pk. lambda pk: reverse('photos:delete_photo', kwargs={'pk': pk}), # 개별 게시물 보는 URL name 은 'view_photo'이며, # URL패턴의 그룹은 pk. lambda pk: reverse('photos:view_photo', kwargs={'pk': pk}), # 게시물 목록 URL name 은 'list_photos' lambda : reverse('photos:list_photos'), # 댓글 다는 URL name 은 'create_comment'이며, # URL패턴의 그룹은 게시물의 pk. lambda pk: reverse('photos:create_comment', kwargs={'pk': pk}), # 댓글 지우는는 URL name 은 'delete_comment이며, # URL패턴의 그룹은 댓글의 pk. lambda pk: reverse('photos:delete_comment', kwargs={'pk': pk}), ) def _login(self, username, password): # 로그인 시도. return self.client.post( settings.LOGIN_URL, {'username': username, 'password': password} ) def _add_photo(self, data, follow=True): # 게시물 게시 시도. return self.client.post( self.urls.create_photo(), data=data, follow=follow ) def test_404(self): """없는 페이지에 접근하는 테스트. """ response = self.client.get('/page_not_found/') self.assertEqual(response.status_code, 404) def test_create_photo_by_view_on_logout(self): """로그아웃 상태에서 뷰 함수를 이용해 게시물을 게시하는 테스트. """ _form_data = { 'image': 'test image', 'description': 'test description', } # 로그인하지 않은 상태에서 게시물 게시 시도. response = self._add_photo(_form_data) # 로그인 하지 않았으므로 로그인 URL로 redirect 됐는지 확인. self.assertEqual(response.resolver_match.func.__name__, 'login') self.assertEqual(response.redirect_chain[0][1], 302) def test_create_photo_by_view_on_login(self): """로그인 상태에서 뷰 함수를 이용해 게시물을 게시하는 테스트. """ _form_data = { 'title': 'Sjfdlkja23@#$!@SDF title', 'content': 'FSAD@3@#$!sdflkj content', } self._login(**self.users[0]) # 게시물 게시 시도. response = self._add_photo(_form_data) # 가장 마지막에 등록된 게시물 데이터를 가져온다. latest_photo = models.Photo.objects.latest('pk') # 게시물 게시 후 해당 개별 게시물을 보는 URL로 redirect 했는지 확인. _view_photo_url = self.urls.view_photo(latest_photo.pk) self.assertEqual( response.redirect_chain[0][0], _view_photo_url ) self.assertEqual(response.redirect_chain[0][1], 302) # 이동한 URL의 뷰 함수가 detail_photo 인지 테스트. self.assertEqual( response.resolver_match.func, resolve(_view_photo_url)[0] # see also : https://goo.gl/htu8ko ) # 저장한 게시물의 개별 보기 url로 접근 response = self.client.get(_view_photo_url) # 화면에 사용된 템플릿 컨텍스트의 photo와 latest_photo 의 pk 비교. self.assertEqual( response.context['photo'].pk, latest_photo.pk ) # 화면에 사용된 템플릿 컨텍스트의 photo와 글 작성에 사용한 제목 비교. self.assertEqual( response.context['photo'].title, _form_data['title'] ) @unittest.skip('이 장식자를 제거하며 하나씩 테스트를 통과하세요') def test_create_photo_for_form_errors(self): """뷰 함수를 이용해 게시물을 게시하는 테스트 중 필수 입력 폼 테스트. """ self._login(**self.users[0]) # 필수 입력 항목인 title, content 빠뜨리고 게시물 게시 시도. response = self._add_photo({}) # 게시에 사용한 폼 인스턴스 객체가 템플릿 컨텍스트에 있는지 테스트. self.assertIn('form', response.context) # 템플릿 변수인 form에 필수 폼 필드에 대해 오류가 있는지 테스트. self.assertTrue(response.context['form'].has_error('title')) self.assertTrue(response.context['form'].has_error('content')) # 필수 입력 항목인 title, content 빠뜨리고 게시물 게시 시도. _form_data = {} response = self._add_photo(_form_data) # 게시에 사용한 폼 인스턴스 객체가 템플릿 컨텍스트에 있는지 테스트. self.assertIn('form', response.context) # 템플릿 변수인 form에 필수 폼 필드에 대해 오류가 있는지 테스트. self.assertTrue(response.context['form'].has_error('title')) self.assertTrue(response.context['form'].has_error('content')) # 필수 입력 항목인 content 빠뜨리고 게시물 게시 시도. _form_data['title'] = 'SADFSAFD#$!@#$! title' response = self._add_photo(_form_data) # 게시에 사용한 폼 인스턴스 객체가 템플릿 컨텍스트에 있는지 테스트. self.assertIn('form', response.context) # 템플릿 변수인 form에 필수 폼 필드에 대해 오류가 있는지 테스트. self.assertFalse(response.context['form'].has_error('title')) self.assertTrue(response.context['form'].has_error('content')) # 필수 입력 항목 다 넣고 게시물 게시 시도. _form_data['content'] = 'SADFSAFD#$!@#$! content' response = self._add_photo(_form_data) # 게시에 사용한 폼 인스턴스 객체가 템플릿 컨텍스트에 없는지 테스트. self.assertNotIn('form', response.context) @unittest.skip('이 장식자를 제거하며 하나씩 테스트를 통과하세요') def test_view_photo_not_exists(self): """존재하지 않는 개별 게시물 페이지에 접속하여 404가 뜨는지 테스트. """ # 존재하지 않는 게시물에 접근. response = self.client.get(self.urls.view_photo(9999), follow=True) # http status가 404인지 확인. self.assertEqual(response.status_code, 404) @unittest.skip('이 장식자를 제거하며 하나씩 테스트를 통과하세요') def test_delete_photo_on_logout(self): """로그아웃 상태에서 개별 게시물을 지우는 테스트. """ _form_data = { 'title': 'Sjfdlkja23@#$!@SDF title', 'content': 'FSAD@3@#$!sdflkj content', } self._login(**self.users[0]) # 게시물 게시 시도. response = self._add_photo(_form_data) self.assertIn(response.status_code, (200, 201,)) latest_photo = models.Photo.objects.latest('pk') # test1 로그아웃 self.client.get(settings.LOGOUT_URL) # 로그인하지 않고 삭제 url 접근 response = self.client.get( self.urls.delete_photo(latest_photo.pk), follow=True ) # 로그인 URL로 redirect 됐는지 확인. self.assertEqual(response.resolver_match.func.__name__, 'login') @unittest.skip('이 장식자를 제거하며 하나씩 테스트를 통과하세요') def test_delete_photo_without_permm(self): """권한 없이 개별 게시물을 지우는 시도하는 테스트. """ _form_data = { 'title': 'Sjfdlkja23@#$!@SDF title', 'content': 'FSAD@3@#$!sdflkj content', } self._login(**self.users[0]) # 게시물 게시 시도. response = self._add_photo(_form_data) self.assertIn(response.status_code, (200, 201,)) latest_photo = models.Photo.objects.latest('pk') # test1 로그아웃 self.client.get(settings.LOGOUT_URL) # test2 로그인 self._login(**self.users[1]) _delete_photo_url = self.urls.delete_photo(latest_photo.pk) # http method POST로 접근. response = self.client.photo(_delete_photo_url, follow=True) # 권한이 없으므로 403 status 응답해야 함. self.assertEqual(response.status_code, 403) @unittest.skip('이 장식자를 제거하며 하나씩 테스트를 통과하세요') def test_delete_photo(self): """개별 게시물을 지우는 테스트. """ _form_data = { 'title': 'Sjfdlkja23@#$!@SDF title', 'content': 'FSAD@3@#$!sdflkj content', } self._login(**self.users[0]) # 게시물 게시 시도. response = self._add_photo(_form_data) self.assertIn(response.status_code, (200, 201,)) latest_photo = models.Photo.objects.latest('pk') _delete_photo_url = self.urls.delete_photo(latest_photo.pk) # http method GET으로 접근. response = self.client.get(_delete_photo_url, follow=True) self.assertEqual(response.status_code, 200) # 이동한 URL의 뷰 함수가 delete_photo 인지 테스트. self.assertEqual( response.resolver_match.func, resolve(_delete_photo_url)[0] ) # http method POST로 접근. response = self.client.photo(_delete_photo_url, follow=True) # 삭제 후 글 목록으로 이동했는지 확인 self.assertEqual(response.redirect_chain[0][1], 302) self.assertEqual( response.resolver_match.func, resolve(self.urls.list_photos())[0] ) # 삭제한 게시물 url 접근 시 404인지. response = self.client.get(self.urls.view_photo(latest_photo.pk)) self.assertEqual(response.status_code, 404) # DB에 삭제한 게시물이 존재하는 지 확인 _exists = models.Photo.objects.filter(pk=latest_photo.pk).exists() self.assertFalse(_exists)