Wagtail 사이트 테스트하기¶
Wagtail은 사이트 테스트를 단순화하는 몇 가지 유틸리티를 제공합니다.
WagtailPageTestCase¶
class wagtail.test.utils.WagtailPageTestCase
WagtailPageTestCase 는 django.test.TestCase 를 확장하여 몇 가지 새로운 assert 메서드를 추가합니다. 이 메서드를 사용하려면 이 클래스를 상속해야 합니다:
from wagtail.test.utils import WagtailPageTestCase
from myapp.models import MyPage
class MyPageTests(WagtailPageTestCase):
def test_can_create_a_page(self):
...
assertPageIsRoutable(page, route_path=”/”, msg=None)
page 가 Http404 오류를 발생시키지 않고 라우팅될 수 있는지 확인합니다.
여러 경로를 가진 페이지 유형의 경우, route_path 를 사용하여 테스트할 대체 경로를 지정할 수 있습니다.
이 assertion은 페이지 유형의 커스텀 라우팅 로직에 대한 테스트 커버리지를 얻는 데 좋습니다. 예시:
from wagtail.test.utils import WagtailPageTestCase
from myapp.models import EventListPage
class EventListPageRoutabilityTests(WagtailPageTestCase):
@classmethod
def setUpTestData(cls):
# 테스트용 페이지 생성
...
def test_default_route(self):
self.assertPageIsRoutable(self.page)
def test_year_archive_route(self):
# 참고: 지정된 연도에 이벤트가 없을 때 이 페이지 유형이 404를 발생시키더라도
# 라우팅은 여전히 성공해야 합니다
self.assertPageIsRoutable(self.page, "archive/year/1984/")
assertPageIsRenderable(page, route_path=”/”, query_data=None, post_data=None, user=None, accept_404=False, accept_redirect=False, msg=None)
page 가 치명적인 오류를 발생시키지 않고 렌더링될 수 있는지 확인합니다.
여러 경로를 가진 페이지 유형의 경우, route_path 를 사용하여 페이지의 일반 url 에 추가될 부분 경로를 지정할 수 있습니다.
post_data 가 제공되면, 테스트는 요청 본문에 post_data 를 포함하여 POST 요청을 수행합니다. 그렇지 않으면 GET 요청이 수행됩니다.
query_data 가 제공되면, 항상 쿼리스트링으로 변환되어 요청 URL에 추가됩니다.
user 가 제공되면, 테스트는 해당 사용자를 활성 사용자로 설정하여 수행됩니다.
기본적으로, 페이지 URL에 대한 요청이 301, 302 또는 404 HTTP 응답으로 결과가 나오면 assertion은 실패합니다. 404 응답이 예상되는 페이지/경로를 테스트하는 경우, accept_404=True 를 사용하여 이를 표시할 수 있으며, 이 경우 404 응답을 받았을 때 assertion이 통과합니다. 마찬가지로, 리디렉션 응답이 예상되는 페이지/경로를 테스트하는 경우, accept_redirect=True 를 사용하여 이를 표시할 수 있으며, 이 경우 301 또는 302 응답을 받았을 때 assertion이 통과합니다.
이 assertion은 페이지 유형의 커스텀 렌더링 로직에 대한 테스트 커버리지를 얻는 데 좋습니다. 예시:
def test_default_route_rendering(self):
self.assertPageIsRenderable(self.page)
def test_year_archive_route_with_zero_matches(self):
# 참고: 지정된 연도에 이벤트가 없을 때 404를 발생시켜야 함
self.assertPageIsRenderable(self.page, "archive/year/1984/", accept_404=True)
def test_month_archive_route_with_zero_matches(self):
# 참고: 지정된 월에 이벤트가 없을 때 연도별 뷰로 리디렉션해야 함
self.assertPageIsRenderable(self.page, "archive/year/1984/07/", accept_redirect=True)
assertPageIsEditable(page, post_data=None, user=None, msg=None)
페이지 편집 뷰가 page 에 대해 치명적인 오류 없이 작동하는지 확인합니다.
user 가 제공되면, 테스트는 해당 사용자를 활성 사용자로 설정하여 수행됩니다. 그렇지 않으면, 슈퍼유저가 생성되어 테스트에 사용됩니다.
성공적인 GET 요청 후, 요청 본문에 필드 데이터를 포함한 POST 요청이 수행됩니다. post_data 가 제공되면, 그것이 이 목적으로 사용됩니다. 그렇지 않으면, 이 데이터는 GET 응답 HTML에서 추출됩니다.
이 assertion은 커스텀 필드, 패널 구성 및 커스텀 유효성 검사 로직에 대한 테스트 커버리지를 얻는 데 좋습니다. 예시:
def test_editability(self):
self.assertPageIsEditable(self.page)
def test_editability_on_post(self):
self.assertPageIsEditable(
self.page,
post_data={
"title": "Fabulous events",
"slug": "events",
"show_featured": True,
"show_expired": False,
"action-publish": "",
}
)
assertPageIsPreviewable(page, mode=””, post_data=None, user=None, msg=None)
페이지 미리보기 뷰가 page 에 대해 치명적인 오류 없이 로드될 수 있는지 확인합니다.
다양한 미리보기 모드를 지원하는 페이지 유형의 경우, mode 를 사용하여 테스트할 미리보기 모드를 지정할 수 있습니다.
user 가 제공되면, 테스트는 해당 사용자를 활성 사용자로 설정하여 수행됩니다. 그렇지 않으면, 슈퍼유저가 생성되어 테스트에 사용됩니다.
미리보기를 로드하기 위해, 테스트 클라이언트는 요청 본문에 모든 필수 필드 데이터를 포함하여 POST 요청을 수행해야 합니다. post_data 가 제공되면, 그것이 이 목적으로 사용됩니다. 그렇지 않으면, 이 메서드는 페이지 편집 뷰에서 이 데이터를 추출하려고 시도합니다.
이 assertion은 커스텀 미리보기 모드에 대한 테스트 커버리지를 얻거나, 커스텀 렌더링 로직이 Wagtail의 미리보기 모드와 호환되는지 확인하는 데 좋습니다. 예시:
def test_general_previewability(self):
self.assertPageIsPreviewable(self.page)
def test_archive_previewability(self):
self.assertPageIsPreviewable(self.page, mode="year-archive")
assertCanCreateAt(parent_model, child_model, msg=None)
특정 자식 페이지 유형이 부모 페이지 유형 아래에 생성될 수 있는지 확인합니다. parent_model 과 child_model 은 테스트 중인 페이지 클래스여야 합니다.
def test_can_create_under_home_page(self):
# HomePage 아래에 ContentPage를 생성할 수 있습니다
self.assertCanCreateAt(HomePage, ContentPage)
assertCanNotCreateAt(parent_model, child_model, msg=None)
특정 자식 페이지 유형이 부모 페이지 유형 아래에 생성될 수 없는지 확인합니다. parent_model 과 child_model 은 테스트 중인 페이지 클래스여야 합니다.
def test_cant_create_under_event_page(self):
# EventPage 아래에 ContentPage를 생성할 수 없습니다
self.assertCanNotCreateAt(EventPage, ContentPage)
assertCanCreate(parent, child_model, data, msg=None, publish=True) 제공된 POST 데이터를 사용하여 주어진 페이지 유형의 자식이 부모 아래에 생성될 수 있는지 확인합니다.
parent 는 페이지 인스턴스여야 하고, child_model 은 페이지 하위 클래스여야 합니다. data 는 Wagtail 관리자 페이지 생성 메서드에 POST할 딕셔너리여야 합니다.
publish 는 생성되는 페이지가 게시되어야 하는지 여부를 지정합니다 - 기본값은 True 입니다.
from wagtail.test.utils.form_data import nested_form_data, streamfield
def test_can_create_content_page(self):
# HomePage 가져오기
root_page = HomePage.objects.get(pk=2)
# 이 POST 데이터로 여기에 ContentPage를 만들 수 있는지 확인
self.assertCanCreate(root_page, ContentPage, nested_form_data({
'title': 'About us',
'body': streamfield([
('text', 'Lorem ipsum dolor sit amet'),
])
}))
POST 데이터 구성에 유용한 함수 세트는 폼 데이터 헬퍼를 참조하세요.
assertAllowedParentPageTypes(child_model, parent_models, msg=None)
child_model 이 생성될 수 있는 유일한 페이지 유형이 parent_models 인지 테스트합니다.
허용된 부모 모델 목록은 부모 모델이 Page.subpage_types 를 설정한 경우 Page.parent_page_types 에 설정된 것과 다를 수 있습니다.
def test_content_page_parent_pages(self):
# ContentPage는 HomePage 또는 다른 ContentPage 아래에서만 생성될 수 있습니다
self.assertAllowedParentPageTypes(
ContentPage, {HomePage, ContentPage})
# EventPage는 EventIndex 아래에서만 생성될 수 있습니다
self.assertAllowedParentPageTypes(
EventPage, {EventIndex})
assertAllowedSubpageTypes(parent_model, child_models, msg=None)
parent_model 아래에 생성될 수 있는 유일한 페이지 유형이 child_models 인지 테스트합니다.
허용된 자식 모델 목록은 자식 모델이 Page.parent_page_types 를 설정한 경우 Page.subpage_types 에 설정된 것과 다를 수 있습니다.
def test_content_page_subpages(self):
# ContentPage는 다른 ContentPage 자식만 가질 수 있습니다
self.assertAllowedSubpageTypes(
ContentPage, {ContentPage})
# HomePage는 ContentPage와 EventIndex 자식을 가질 수 있습니다
self.assertAllowedSubpageTypes(
HomePage, {ContentPage, EventIndex})
폼 데이터 헬퍼¶
The assertCanCreate method requires page data to be passed in
the same format that the page edit form would submit. For complex
page types, it can be difficult to construct this data structure by hand;
the wagtail.test.utils.form_data module provides a set of helper
functions to assist with this.
- wagtail.test.utils.form_data.nested_form_data(data)¶
Translates a nested dict structure into a flat form data dict with hyphen-separated keys.
nested_form_data({ 'foo': 'bar', 'parent': { 'child': 'field', }, }) # Returns: {'foo': 'bar', 'parent-child': 'field'}
- wagtail.test.utils.form_data.rich_text(value, editor='default', features=None)¶
Converts an HTML-like rich text string to the data format required by the currently active rich text editor.
- 매개변수:
editor – An alternative editor name as defined in
WAGTAILADMIN_RICH_TEXT_EDITORSfeatures – A list of features allowed in the rich text content (see 서식 있는 텍스트 필드의 기능 제한)
self.assertCanCreate(root_page, ContentPage, nested_form_data({ 'title': 'About us', 'body': rich_text('<p>Lorem ipsum dolor sit amet</p>'), }))
- wagtail.test.utils.form_data.streamfield(items)¶
Takes a list of (block_type, value) tuples and turns it in to StreamField form data. Use this within a
nested_form_data()call, with the field name as the key.nested_form_data({'content': streamfield([ ('text', 'Hello, world'), ])}) # Returns: # { # 'content-count': '1', # 'content-0-type': 'text', # 'content-0-value': 'Hello, world', # 'content-0-order': '0', # 'content-0-deleted': '', # }
- wagtail.test.utils.form_data.inline_formset(items, initial=0, min=0, max=1000)¶
Takes a list of form data for an InlineFormset and translates it in to valid POST data. Use this within a
nested_form_data()call, with the formset relation name as the key.nested_form_data({'lines': inline_formset([ {'text': 'Hello'}, {'text': 'World'}, ])}) # Returns: # { # 'lines-TOTAL_FORMS': '2', # 'lines-INITIAL_FORMS': '0', # 'lines-MIN_NUM_FORMS': '0', # 'lines-MAX_NUM_FORMS': '1000', # 'lines-0-text': 'Hello', # 'lines-0-ORDER': '0', # 'lines-0-DELETE': '', # 'lines-1-text': 'World', # 'lines-1-ORDER': '1', # 'lines-1-DELETE': '', # }
테스트 내에서 Page 객체 생성하기¶
테스트 내에서 페이지 객체를 생성하려면, 실제로 테스트하려는 페이지를 생성하기 전에 몇 가지 단계를 거쳐야 합니다.
페이지는 일반 Django 모델처럼
MyPage.objects.create()로 직접 생성할 수 없으며,parent.add_child(instance=child)를 사용하여 부모 페이지의 자식으로 추가해야 합니다.페이지 트리를 시작하려면
Page.get_first_root_node()로 생성할 수 있는 루트 페이지가 필요합니다.또한 올바른
hostname과root_page로 설정된Site가 필요합니다.
from wagtail.models import Page, Site
from wagtail.rich_text import RichText
from wagtail.test.utils import WagtailPageTestCase
from home.models import HomePage, MyPage
class MyPageTest(WagtailPageTestCase):
@classmethod
def setUpTestData(cls):
root = Page.get_first_root_node()
Site.objects.create(
hostname="testserver",
root_page=root,
is_default_site=True,
site_name="testserver",
)
home = HomePage(title="Home")
root.add_child(instance=home)
cls.page = MyPage(
title="My Page",
slug="mypage",
)
home.add_child(instance=cls.page)
def test_get(self):
response = self.client.get(self.page.url)
self.assertEqual(response.status_code, 200)
페이지 콘텐츠 작업하기¶
페이지의 콘텐츠를 테스트하고 싶을 것입니다. StreamField 를 포함하는 경우, 블록의 이름과 콘텐츠를 포함하는 튜플 목록으로 콘텐츠를 설정해야 합니다. RichTextBlock 의 경우, 콘텐츠는 RichText 의 인스턴스여야 합니다.
...
from wagtail.rich_text import RichText
class MyPageTest(WagtailPageTestCase):
@classmethod
def setUpTestData(cls):
...
# 여기서 페이지 인스턴스 생성
cls.page.body.extend(
[
("heading", "Just a CharField Heading"),
("paragraph", RichText("<p>First paragraph</p>")),
("paragraph", RichText("<p>Second paragraph</p>")),
]
)
cls.page.save()
def test_page_content(self):
response = self.client.get(self.page.url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Just a CharField Heading")
self.assertContains(response, "<p>First paragraph</p>")
self.assertContains(response, "<p>Second paragraph</p>")
픽스처¶
dumpdata 사용하기¶
테스트를 위한 픽스처 생성은 개발 환경에서 콘텐츠를 생성한 다음 Django의 dumpdata 명령을 사용하는 것이 가장 좋습니다.
기본적으로 dumpdata 는 content_type 을 기본 키로 표현한다는 점에 유의하세요. 이는 모델을 추가/제거할 때 일관성 문제를 일으킬 수 있습니다. 콘텐츠 유형은 픽스처와 별도로 채워지기 때문입니다. 이를 방지하려면 --natural-foreign 스위치를 사용하세요. 이는 콘텐츠 유형을 기본 키 대신 ["app", "model"] 로 표현합니다.
수동 수정¶
덤프된 픽스처를 수동으로 수정하거나 모두 직접 작성할 수도 있습니다. 주의해야 할 몇 가지 사항이 있습니다.
커스텀 페이지 모델¶
픽스처에서 커스텀 페이지 모델을 생성할 때, wagtailcore.page 항목과 커스텀 페이지 모델 항목을 모두 추가해야 합니다.
website 모듈에 Homepage(Page) 클래스를 정의했다고 가정해 봅시다.
다음과 같이 픽스처에서 이러한 홈페이지를 생성할 수 있습니다:
[
{
"model": "wagtailcore.page",
"pk": 3,
"fields": {
"title": "My Customer's Homepage",
"content_type": ["website", "homepage"],
"depth": 2
}
},
{
"model": "website.homepage",
"pk": 3,
"fields": {}
}
]
Treebeard 필드¶
path / numchild / depth 필드를 채우는 것은 get_parent() 와 같은 트리 연산이 올바르게 작동하기 위해 필요합니다.
url_path 는 채워지지 않으면 일부 드문 경우에 오류를 일으킬 수 있는 또 다른 필드입니다.
Treebeard 문서는 이것이 어떻게 작동하는지 이해하는 데 도움이 될 수 있습니다.