繁体   English   中英

Django 3.0.8 测试用例失败

[英]Django 3.0.8 Test Cases Failing

我正在尝试遵循简单优于复杂的教程。 具体来说,我刚刚完成了第 4 部分 我在运行测试时遇到以下 4 种故障:

$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.............................FFF.F..................................
======================================================================
FAIL: test_contains_form (accounts.tests.test_view_password_reset.PasswordResetTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\admin\Desktop\Development\Django\myproject\myproject\accounts\tests\tes
t_view_password_reset.py", line 30, in test_contains_form
    self.assertIsInstance(form, PasswordResetForm)
AssertionError: None is not an instance of <class 'django.contrib.auth.forms.PasswordRes
etForm'>

======================================================================
FAIL: test_csrf (accounts.tests.test_view_password_reset.PasswordResetTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\admin\Desktop\Development\Django\myproject\myproject\accounts\tests\tes
t_view_password_reset.py", line 26, in test_csrf
    self.assertContains(self.response, 'csrfmiddlewaretoken')
  File "C:\Users\admin\Desktop\Development\Django\myproject\venv\lib\site-packages\djang
o\test\testcases.py", line 454, in assertContains
    self.assertTrue(real_count != 0, msg_prefix + "Couldn't find %s in response" % text_
repr)
AssertionError: False is not true : Couldn't find 'csrfmiddlewaretoken' in response

======================================================================
FAIL: test_form_inputs (accounts.tests.test_view_password_reset.PasswordResetTests)
The view must contain two inputs: csrf and email
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\admin\Desktop\Development\Django\myproject\myproject\accounts\tests\tes
t_view_password_reset.py", line 36, in test_form_inputs
    self.assertContains(self.response, '<input', 2)
  File "C:\Users\admin\Desktop\Development\Django\myproject\venv\lib\site-packages\djang
o\test\testcases.py", line 449, in assertContains
    self.assertEqual(
AssertionError: 0 != 2 : Found 0 instances of '<input' in response (expected 2)

======================================================================
FAIL: test_view_function (accounts.tests.test_view_password_reset.PasswordResetTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\admin\Desktop\Development\Django\myproject\myproject\accounts\tests\tes
t_view_password_reset.py", line 23, in test_view_function
    self.assertEquals(view.func.view_class, auth_views.PasswordResetView)
AssertionError: <class 'django.contrib.auth.views.PasswordResetDoneView'> != <class 'dja
ngo.contrib.auth.views.PasswordResetView'>

----------------------------------------------------------------------
Ran 68 tests in 9.235s

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

这是我的文件结构:

├───myproject
│   │   db.sqlite3
│   │   manage.py
│   │   
│   ├───accounts
│   │   │   admin.py
│   │   │   apps.py
│   │   │   forms.py
│   │   │   models.py
│   │   │   views.py
│   │   │   __init__.py
│   │   │   
│   │   ├───migrations
│   │   │   │   __init__.py
│   │   │   │   
│   │   │   └───__pycache__
│   │   │           
│   │   ├───tests
│   │   │   │   test_form_signup.py
│   │   │   │   test_mail_password_reset.py
│   │   │   │   test_view_password_change.py
│   │   │   │   test_view_password_reset.py
│   │   │   │   test_view_signup.py
│   │   │   │   __init__.py
│   │   │   │   
│   │   │   └───__pycache__
│   │   │           
│   │   └───__pycache__
│   │           
│   ├───boards
│   │   │   admin.py
│   │   │   apps.py
│   │   │   forms.py
│   │   │   models.py
│   │   │   views.py
│   │   │   __init__.py
│   │   │   
│   │   ├───migrations
│   │   │   │   0001_initial.py
│   │   │   │   __init__.py
│   │   │   │   
│   │   │   └───__pycache__
│   │   │           
│   │   ├───templatetags
│   │   │   │   form_tags.py
│   │   │   │   __init__.py
│   │   │   │   
│   │   │   └───__pycache__
│   │   │           
│   │   ├───tests
│   │   │   │   test_templatetags.py
│   │   │   │   test_views.py
│   │   │   │   __init__.py
│   │   │   │   
│   │   │   └───__pycache__
│   │   │           
│   │   └───__pycache__
│   │           
│   ├───myproject
│   │   │   asgi.py
│   │   │   settings.py
│   │   │   urls.py
│   │   │   wsgi.py
│   │   │   __init__.py
│   │   │   
│   │   └───__pycache__
│   │           
│   ├───static
│   │   ├───css
│   │   │       accounts.css
│   │   │       app.css
│   │   │       bootstrap.min.css
│   │   │       
│   │   ├───img
│   │   │       moroccan-flower-dark.png
│   │   │       y-so-serious.png
│   │   │       
│   │   ├───js
│   │   │       bootstrap.min.js
│   │   │       jquery-3.5.1.min.js
│   │   │       popper.min.js
│   │           
│   └───templates
│       │   base.html
│       │   base_accounts.html
│       │   home.html
│       │   login.html
│       │   new_topic.html
│       │   password_change.html
│       │   password_change_done.html
│       │   password_reset.html
│       │   password_reset_complete.html
│       │   password_reset_confirm.html
│       │   password_reset_done.html
│       │   password_reset_email.html
│       │   password_reset_subject.txt
│       │   signup.html
│       │   topics.html
│       │   
│       └───includes
│               form.html

这是myproject\\accounts\\tests\\test_view_password_reset.py中似乎抛出错误的部分:

from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.contrib.auth import views as auth_views
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
from django.contrib.auth.models import User
from django.core import mail
from django.urls import reverse
from django.urls import resolve
from django.test import TestCase


class PasswordResetTests(TestCase):
    def setUp(self):
        url = reverse('password_reset_done')
        self.response = self.client.get(url)

    def test_status_code(self):
        self.assertEquals(self.response.status_code, 200)

    def test_view_function(self):
        view = resolve('/reset/done/')
        self.assertEquals(view.func.view_class, auth_views.PasswordResetView)

    def test_csrf(self):
        self.assertContains(self.response, 'csrfmiddlewaretoken')

    def test_contains_form(self):
        form = self.response.context.get('form')
        self.assertIsInstance(form, PasswordResetForm)

    def test_form_inputs(self):
        '''
        The view must contain two inputs: csrf and email
        '''
        self.assertContains(self.response, '<input', 2)
        self.assertContains(self.response, 'type="email"', 1)


class SuccessfulPasswordResetTests(TestCase):
    def setUp(self):
        email = 'john@doe.com'
        User.objects.create_user(username='john', email=email, password='123abcdef')
        url = reverse('password_reset')
        self.response = self.client.post(url, {'email': email})

    def test_redirection(self):
        '''
        A valid form submission should redirect the user to `password_reset_done` view
        '''
        url = reverse('password_reset_done')
        self.assertRedirects(self.response, url)

    def test_send_password_reset_email(self):
        self.assertEqual(1, len(mail.outbox))


class InvalidPasswordResetTests(TestCase):
    def setUp(self):
        url = reverse('password_reset')
        self.response = self.client.post(url, {'email': 'donotexist@email.com'})

    def test_redirection(self):
        '''
        Even invalid emails in the database should
        redirect the user to `password_reset_done` view
        '''
        url = reverse('password_reset_done')
        self.assertRedirects(self.response, url)

    def test_no_reset_email_sent(self):
        self.assertEqual(0, len(mail.outbox))

class PasswordResetDoneTests(TestCase):
    def setUp(self):
        url = reverse('password_reset_done')
        self.response = self.client.get(url)

    def test_status_code(self):
        self.assertEquals(self.response.status_code, 200)

    def test_view_function(self):
        view = resolve('/reset/done/')
        self.assertEquals(view.func.view_class, auth_views.PasswordResetDoneView)

class PasswordResetConfirmTests(TestCase):
    def setUp(self):
        user = User.objects.create_user(username='john', email='john@doe.com', password='123abcdef')

        '''
        create a valid password reset token
        based on how django creates the token internally:
        https://github.com/django/django/blob/1.11.5/django/contrib/auth/forms.py#L280
        '''
        self.uid = urlsafe_base64_encode(force_bytes(user.pk))
        self.token = default_token_generator.make_token(user)

        url = reverse('password_reset_confirm', kwargs={'uidb64': self.uid, 'token': self.token})
        self.response = self.client.get(url, follow=True)

    def test_status_code(self):
        self.assertEquals(self.response.status_code, 200)

    def test_view_function(self):
        view = resolve('/reset/{uidb64}/{token}/'.format(uidb64=self.uid, token=self.token))
        self.assertEquals(view.func.view_class, auth_views.PasswordResetConfirmView)

    def test_csrf(self):
        self.assertContains(self.response, 'csrfmiddlewaretoken')

    def test_contains_form(self):
        form = self.response.context.get('form')
        self.assertIsInstance(form, SetPasswordForm)

    def test_form_inputs(self):
        '''
        The view must contain two inputs: csrf and two password fields
        '''
        self.assertContains(self.response, '<input', 3)
        self.assertContains(self.response, 'type="password"', 2)


class InvalidPasswordResetConfirmTests(TestCase):
    def setUp(self):
        user = User.objects.create_user(username='john', email='john@doe.com', password='123abcdef')
        uid = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)

        '''
        invalidate the token by changing the password
        '''
        user.set_password('abcdef123')
        user.save()

        url = reverse('password_reset_confirm', kwargs={'uidb64': uid, 'token': token})
        self.response = self.client.get(url)

    def test_status_code(self):
        self.assertEquals(self.response.status_code, 200)

    def test_html(self):
        password_reset_url = reverse('password_reset')
        self.assertContains(self.response, 'invalid password reset link')
        self.assertContains(self.response, 'href="{0}"'.format(password_reset_url))

class PasswordResetCompleteTests(TestCase):
    def setUp(self):
        url = reverse('password_reset_complete')
        self.response = self.client.get(url)

    def test_status_code(self):
        self.assertEquals(self.response.status_code, 200)

    def test_view_function(self):
        view = resolve('/reset/complete/')
        self.assertEquals(view.func.view_class, auth_views.PasswordResetCompleteView)

我的项目\\网址.py

from django.contrib import admin
from django.urls import path
from django.contrib.auth import views as auth_views

from accounts import views as accounts_views
from boards import views
from django.conf.urls import url

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^signup/$', accounts_views.signup, name='signup'),
    url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
    url(r'^logout/$', auth_views.LogoutView.as_view(), name='logout'),
    url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'),
    url(r'^boards/(?P<pk>\d+)/new/$', views.new_topic, name='new_topic'),
    path('admin/', admin.site.urls),

    #Password Reset URLs
    url(r'^reset/$',
        auth_views.PasswordResetView.as_view(
            template_name='password_reset.html',
            email_template_name='password_reset_email.html',
            subject_template_name='password_reset_subject.txt'
        ),
        name='password_reset'),
    url(r'^reset/done/$',
        auth_views.PasswordResetDoneView.as_view(template_name='password_reset_done.html'),
        name='password_reset_done'),
    url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        auth_views.PasswordResetConfirmView.as_view(template_name='password_reset_confirm.html'),
        name='password_reset_confirm'),
    url(r'^reset/complete/$',
        auth_views.PasswordResetCompleteView.as_view(template_name='password_reset_complete.html'),
        name='password_reset_complete'),

    url(r'^settings/password/$', auth_views.PasswordChangeView.as_view(template_name='password_change.html'),
        name='password_change'),
    url(r'^settings/password/done/$', auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'),
        name='password_change_done'),
]

帐户\\视图.py

from django.contrib.auth import login as auth_login
from django.shortcuts import render, redirect

from .forms import SignUpForm

# Create your views here.
def signup(request):
    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            user = form.save()
            auth_login(request, user)
            return redirect('home')
    else:
        form = SignUpForm()
    return render(request, 'signup.html', {'form': form})

板\\views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.http import Http404
from django.contrib.auth.models import User
from django.shortcuts import render, redirect, get_object_or_404
from .forms import NewTopicForm
from .models import Board, Topic, Post

# Create your views here.
def home(request):
    boards = Board.objects.all()
    return render(request, 'home.html', {'boards': boards})

def board_topics(request, pk):
    board = get_object_or_404(Board, pk=pk)
    return render(request, 'topics.html', {'board': board})

def new_topic(request, pk):
    board = get_object_or_404(Board, pk=pk)
    user = User.objects.first()  # TODO: get the currently logged in user
    if request.method == 'POST':
        form = NewTopicForm(request.POST)
        if form.is_valid():
            topic = form.save(commit=False)
            topic.board = board
            topic.starter = user
            topic.save()
            post = Post.objects.create(
                message=form.cleaned_data.get('message'),
                topic=topic,
                created_by=user
            )
            return redirect('board_topics', pk=board.pk)  # TODO: redirect to the created topic page
    else:
        form = NewTopicForm()
    return render(request, 'new_topic.html', {'board': board, 'form': form})

模板\\base.html

{% load static %}<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Django Boards{% endblock %}</title>
    <link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/app.css' %}">
    {% block stylesheet %}{% endblock %}
</head>
<body>
    {% block body %}
        <nav class="navbar navbar-expand-sm navbar-dark bg-dark">
            <div class="container">

                <a class="navbar-brand" href="{% url 'home' %}">Django Boards</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="mainMenu">
                    {% if user.is_authenticated %}
                        <ul class="navbar-nav ml-auto">
                            <li class="nav-item dropdown">
                                <a class="nav-link dropdown-toggle" href="#" id="userMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                    {{ user.username }}
                                </a>
                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="userMenu">
                                    <a class="dropdown-item" href="#">My account</a>
                                    <a class="dropdown-item" href="#">Change password</a>
                                    <div class="dropdown-divider"></div>
                                    <a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
                                </div>
                            </li>
                        </ul>
                    {% else %}
                        <form class="form-inline ml-auto">
                            <a href="{% url 'login' %}" class="btn btn-outline-secondary">Log in</a>
                            <a href="{% url 'signup' %}" class="btn btn-primary ml-2">Sign up</a>
                        </form>
                    {% endif %}
                </div>

            </div>
        </nav>

        <div class="container">
            <ol class="breadcrumb my-4">
                {% block breadcrumb %}
                {% endblock %}
            </ol>
            {% block content %}
            {% endblock %}
        </div>
    {% endblock body %}

    <script src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>

</body>
</html>

模板\\base_accounts.html

{% extends 'base.html' %}

{% load static %}

{% block stylesheet %}
    <link rel="stylesheet" href="{% static 'css/accounts.css' %}">
{% endblock %}

{% block body %}
    <div class="container">

        <h1 class="text-center logo my-4">
            <a href="{% url 'home' %}">Django Boards</a>
        </h1>
        
        {% block content %}
        {% endblock %}

    </div>
{% endblock %}

模板\\包括\\form.html

{% load form_tags widget_tweaks %}

{% if form.non_field_errors %}
    <div class="alert alert-danger" role="alert">
        {% for error in form.non_field_errors %}
            <p{% if forloop.last %} class="mb-0"{% endif %}>{{ error }}</p>
        {% endfor %}
    </div>
{% endif %}

{% for field in form %}
    <div class="form-group">

        {{ field.label_tag }}
        {% render_field field class=field|input_class %}

        {% for error in field.errors %}
            <div class="invalid-feedback">
                {{ error }}
            </div>
        {% endfor %}

        {% if field.help_text %}
            <small class="form-text text-muted">
                {{ field.help_text|safe }}
            </small>
        {% endif %}
        
    </div>
{% endfor %}

模板\\password_reset.html

{% extends 'base_accounts.html' %}

{% block title %}Reset your password{% endblock %}

{% block content %}
    <div class="row justify-content-center">
        <div class="col-lg-4 col-md-6 col-sm-8">

            <div class="card">
                <div class="card-body">
                    <h3 class="card-title">Reset your password</h3>
                    <p>Enter your email address and we will send you a link to reset your password.</p>
                    <form method="post" novalidate>
                        {% csrf_token %}
                        {% include 'includes/form.html' %}
                        <button type="submit" class="btn btn-primary btn-block">Send password reset email</button>
                    </form>
                </div>
            </div>
            
        </div>
    </div>
{% endblock %}

模板\\password_reset_complete.html

{% extends 'base_accounts.html' %}

{% block title %}Password changed!{% endblock %}

{% block content %}
    <div class="row justify-content-center">
        <div class="col-lg-6 col-md-8 col-sm-10">

            <div class="card">
                <div class="card-body">
                    <h3 class="card-title">Password changed!</h3>
                    <div class="alert alert-success" role="alert">
                        You have successfully changed your password! You may now proceed to log in.
                    </div>
                    <a href="{% url 'login' %}" class="btn btn-secondary btn-block">Return to log in</a>
                </div>
            </div>
            
        </div>
    </div>
{% endblock %}

模板\\password_reset_confirm.html

{% extends 'base_accounts.html' %}

{% block title %}
    {% if validlink %}
        Change password for {{ form.user.username }}
    {% else %}
        Reset your password
    {% endif %}
{% endblock %}

{% block content %}
    <div class="row justify-content-center">
        <div class="col-lg-6 col-md-8 col-sm-10">

            <div class="card">
                <div class="card-body">
                    {% if validlink %}
                        <h3 class="card-title">Change password for @{{ form.user.username }}</h3>
                        <form method="post" novalidate>
                            {% csrf_token %}
                            {% include 'includes/form.html' %}
                            <button type="submit" class="btn btn-success btn-block">Change password</button>
                        </form>
                    {% else %}
                        <h3 class="card-title">Reset your password</h3>
                        <div class="alert alert-danger" role="alert">
                            It looks like you clicked on an invalid password reset link. Please try again.
                        </div>
                        <a href="{% url 'password_reset' %}" class="btn btn-secondary btn-block">Request a new password reset link</a>
                    {% endif %}
                </div>
            </div>
            
        </div>
    </div>
{% endblock %}

模板\\password_reset_done.html

{% extends 'base_accounts.html' %}

{% block title %}Reset your password{% endblock %}

{% block content %}
    <div class="row justify-content-center">
        <div class="col-lg-4 col-md-6 col-sm-8">
            
            <div class="card">
                <div class="card-body">
                    <h3 class="card-title">Reset your password</h3>
                    <p>Check your email for a link to reset your password. If it doesn't appear within a few minutes, check your spam folder.</p>
                    <a href="{% url 'login' %}" class="btn btn-secondary btn-block">Return to log in</a>
                </div>
            </div>

        </div>
    </div>
{% endblock %}

模板\\登录.html

{% extends 'base_accounts.html' %}

{% block title %}Log in to Django Boards{% endblock %}

{% block content %}
    <div class="row justify-content-center">
        <div class="col-lg-4 col-md-6 col-sm-8">

            <div class="card">
                <div class="card-body">
                    <h3 class="card-title">Log in</h3>
                    <form method="post" novalidate>
                        {% csrf_token %}
                        {% include 'includes/form.html' %}
                        <button type="submit" class="btn btn-primary btn-block">Log in</button>
                    </form>
                </div>

                <div class="card-footer text-muted text-center">
                New to Django Boards? <a href="{% url 'signup' %}">Sign up</a>
                </div>
            </div>

            <div class="text-center py-2">
                <small>
                    <a href="#" class="text-muted">Forgot your password?</a>
                </small>
            </div>

        </div>
    </div>
{% endblock %}

我的项目\\网址.py

改变这一行
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$'


url(r'^reset/<uidb64>/<token>/'

如果仍然卡住,则添加$
url(r'^reset/<uidb64>/<token>/$'

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM