funkid
funkid

Reputation: 665

Unit test fails on wtforms

When using pytest to do unittesting on posting datas to flask wtforms, the form's fileds' data is always '', which seems like the data is not properly "posted" to the forms, and it cause the form.validate_on_submit() will always return False. I've disabled the WTF_CSRF_ENABLED when testing.

I've made a minimal project to demonstrate this problem. I did not include database here because database works fine in my real project.

This minimal project structure is here:

.
├── app.py
├── templates
│   └── login.html
└── tests
    └── test_login.py

test_login.py:

import unittest
from flask import current_app
from app import create_app


class BasicsTestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app()
        self.app.config['WTF_CSRF_ENABLED'] = False
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client()

    def tearDown(self):
        self.app_context.pop()

    def test_app_exists(self):
        self.assertFalse(current_app is None)

    def test_home_page(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

    def test_login(self):
        response = self.client.post(
            '/login', data={
                'username': 'a',
                'password': 'a'
            })

app.py:

from flask import Flask, redirect, render_template, url_for, Blueprint
from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, SubmitField
from wtforms.validators import DataRequired, Length

bp = Blueprint('myapp', __name__)


@bp.route('/')
def home():
    return 'hello'


@bp.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm(prefix='form-login-')
    if form.validate_on_submit():
        return redirect(url_for('home'))
    return render_template('login.html', form=form)


class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Log In')


def create_app():
    app = Flask('__name__')
    app.config['SECRET_KEY'] = 'secretkey'
    app.register_blueprint(bp)
    return app

Upvotes: 0

Views: 1315

Answers (1)

funkid
funkid

Reputation: 665

It turns out that when your wtforms have the prefix property, the way you post data should change.

As you see, the form I use is form = LoginForm(prefix='form-login-'), and the way I post the data to the form is

response = self.client.post(
            '/login', data={
                'username': 'a',
                'password': 'a'
            })

It will not pass data properly until you add the prefix you use in the form to the data dictionary, like this:

response = self.client.post(
            '/login', data={
                'form-login-username': 'a',
                'form-login-password': 'a'
            })

Everything works fine now!

But if you want to get the form field data, you should still use form.username.data, without any prefix.

Upvotes: 2

Related Questions