如何在单元测试中将QuerySelectField表单数据发送到Flask视图?

时间:2020-06-02 16:52:18

标签: python flask flask-wtforms flask-testing

我正在尝试测试正在编辑的Flask应用程序中的编辑和添加视图。部署了一个版本的网站,并且视图正常运行,但是我进行的测试似乎未正确传递QuerySelectField数据。另外,在测试中,我会检查表单数据是否通过验证,因此应该通过。

以下是测试:

class TestingWhileLoggedIn(TestCase):
    def create_app(self):
        app = c_app(TestConfiguration)
        return app

    # executed prior to each test
    def setUp(self):
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()

        login(self.client, '******', '******')

    # executed after each test
    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

        logout(self.client)

    def test_add_post_page_li(self):
        p_cat = PostCategory(name='Resources')
        p_cat1 = PostCategory(name='Ressdgources')
        p_cat2 = PostCategory(name='Ressdgsdgources')
        p_cat3 = PostCategory(name='Reurces')
        db.session.add(p_cat)
        db.session.add(p_cat1)
        db.session.add(p_cat2)
        db.session.add(p_cat3)
        db.session.commit()

        all_cats = PostCategory.query.all()

        self.assertEqual([p_cat,p_cat1,p_cat2,p_cat3], all_cats)

        response = self.client.get('/add_post', follow_redirects=False)
        self.assertEqual(response.status_code, 200)

        data = dict(title='Hello', content='fagkjkjas', category=p_cat)

        form = PostForm(data=data)

        # this test passes!
        self.assertEqual(form.validate(), True)

        # printing the data to see what it is
        print(form.data)

        response_1 = self.client.post('/add_post', follow_redirects=False, data=form.data, content_type='multipart/form-data')

        # this one fails
        self.assertEqual(response_1.status_code, 302)

        new_post = db.session.query(Post).filter_by(name='Hello').first()

        self.assertNotEqual(new_post, None)

下面是测试的终端输出。最后两个失败与我要发布的失败相同,因此我将其排除在外。

.......................................{'title': None, 'category': None, 'content': None, 'submit': False}
{'title': 'Hello', 'category': <PostCategory 'Resources'>, 'content': 'fagkjkjas', 'submit': False}
{'title': 'Hello', 'category': None, 'content': 'fagkjkjas', 'submit': True}
F.F.......F..
======================================================================
FAIL: test_add_post_page_li (__main__.TestingWhileLoggedIn)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 165, in test_add_post_page_li
    self.assertEqual(response_1.status_code, 302)
AssertionError: 200 != 302

字典的打印输出来自一些我为了帮助我理解 问题。第一个字典是没有表单提交到add_post视图时,第二个字典是来自测试,其中显示了填写的类别字段,最后一个字典是来自add_post视图,其中显示了未填写的类别。

下面是add_post视图:

@blogs.route('/add_post', methods=['GET', 'POST'])
def add_post():
    """
    Add a blog post
    """
    if not session.get('logged_in'):
        return redirect(url_for('other.home'))

    form = PostForm()

    print(form.data)

    if form.validate_on_submit():
        new_post = Post(name=form.title.data, content=form.content.data, category_id=form.category.data.id, category=form.category.data)

        print('hello')

        try:
            db.session.add(new_post)
            db.session.commit()
        except:
            # not the best behaviour and should change
            return redirect(url_for('other.home'))

        return redirect(url_for('other.home'))

    return render_template('add_post.html', form=form)

这是包含PostForm的forms.py文件

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from ..models import PostCategory

#
# This function is designed to obtain choices for the categories in the PostForm.
#
def category_choices() :
    return PostCategory.query



#
# PostForm
# Purpose:
#     Gives the user a way to input information into the post table.
#
# Fields:
#     Title: String Field (Required)
#     Category: QuerySelectField (Required)
#       Obtains the categories from the database. As of right now there exists
#       only two categories (Travel and Projects)
#
#     Content: Text Field (Required)
#     Submit: Submit Field
#
class PostForm(FlaskForm):
    """
    Form that lets the user add a new post
    """

    title = StringField('Title', validators=[DataRequired()])
    category = QuerySelectField('Category', validators=[DataRequired()], query_factory=category_choices)
    content = TextAreaField('Content', validators=[DataRequired()])

    submit = SubmitField('Submit')


#
# PostCategoryForm
# Purpose:
#     allows user to add new subcategories
#
# Fields:
#     Name: String Field (Required)
#     Submit: SubmitField
#
class PostCategoryForm(FlaskForm) :
    """
    Form used to submit new subcategories
    """
    name = StringField('Name', validators=[DataRequired()])

    submit = SubmitField('Submit')

下面是配置文件

import os
from os.path import abspath, dirname, join

# _cwd = dirname(abspath(__file__))

_basedir = os.path.abspath(os.path.dirname(__file__))


TOP_LEVEL_DIR = os.path.abspath(os.curdir)

class Config(object) :
    pass

class BaseConfiguration(object):
    SQLALCHEMY_TRACK_MODIFICATIONS = False


class ProductionConfiguration(BaseConfiguration):
    SQLALCHEMY_DATABASE_URI = '**********************'
    SQLALCHEMY_POOL_PRE_PING = True
    SQLALCHEMY_ENGINE_OPTIONS = {'pool_recycle' : 3600}
    SECRET_KEY = '******************'
    UPLOAD_FOLDER = TOP_LEVEL_DIR + '/app/static'


class TestConfiguration(BaseConfiguration):
    TESTING = True
    WTF_CSRF_ENABLED = False

    SECRET_KEY = '*************'

    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(_basedir, 'testing.sqlite')

在我看来,wtforms在测试环境中没有发送QuerySelectView,但我不知道为什么。任何帮助表示赞赏。

编辑:在我最初的问题中,我并没有明确指出这只是具有QuerySelectField的表单的问题。没有QuerySelectField的表单正在运行并通过所有测试。

1 个答案:

答案 0 :(得分:0)

Flask不和谐服务器上的一位乐于助人的人能够为我解答。

问题在于Flask-wtforms不会传递模型的整个实例,而只会传递主键。解决方案是只传递数据字典中的主键,如下所示:

import pygame
import time

def create_grid(ROWS, COLS, SCREEN):
    """Creates a grid, sets all values to 0"""
    assert ROWS > 0, "ROWS must be greater than 0"
    assert COLS > 0, "COLS must be greater than 0"

    grid = []
    for i in range(ROWS):
        grid.append([])
        for j in range(COLS):
            grid[i].append(0)
    return grid

pygame.init()

#SCREEN setup
ScreenHeight = 700
ScreenWidth = 700
SCREEN_COLOR = (20, 20, 20)

SCREEN = pygame.display.set_mode((ScreenWidth, ScreenHeight)) #Create Screen
SCREEN.fill(SCREEN_COLOR)


#Number of ROWS and COLUMNS
ROWS = 30
COLS = 40

#How far will the next cube be placed
SQUARESTEPY = ScreenWidth / ROWS
SQUARESTEPX = ScreenWidth / COLS

GREY = (70, 70, 70)
WHITE = (255, 255, 255)

#draw grid
grid = create_grid(ROWS, COLS, SCREEN)

# grid[0][0] = 1
# grid[1][0] = 1
# grid[0][1] = 1

while True:

    #create a copy of the grid to calculate the condition of all cells at the same time
    copy_of_grid = grid[:]

    for ev in pygame.event.get():
        #Quit the game
        if ev.type == pygame.QUIT:
            pygame.quit()

        #if mouse click draws or erases a cell
        if pygame.MOUSEBUTTONDOWN == ev.type:
            posX, posY = pygame.mouse.get_pos()
            print(posX, posY)
            posX, posY = int(posX / SQUARESTEPX), int(posY / SQUARESTEPY)
            grid[posY][posX] = 1 - grid[posY][posX]

    #calculate conway's rules and draw each cell
    for y in range(ROWS):
        for x in range(COLS):
            neighbors = copy_of_grid[(y - 1) % ROWS][(x - 1) % COLS] + \
                        copy_of_grid[y       % ROWS][(x - 1) % COLS] + \
                        copy_of_grid[(y + 1) % ROWS][(x - 1) % COLS] + \
                        copy_of_grid[(y - 1) % ROWS][x       % COLS] + \
                        copy_of_grid[(y + 1) % ROWS][x       % COLS] + \
                        copy_of_grid[(y - 1) % ROWS][(x + 1) % COLS] + \
                        copy_of_grid[y       % ROWS][(x + 1) % COLS] + \
                        copy_of_grid[(y + 1) % ROWS][(x + 1) % COLS]
            #print(x, y, "neighbors: {}, ON: {}".format(neighbors, grid[y][x]))

            #A dead cell surrounded by exactly 3 cells will revive
            if copy_of_grid[y][x] == 0 and (neighbors == 3 or neighbors == 2):
                grid[y][x] = 1

            #A living cell surrounded by less than 2 or more than 3 neighbors wil die
            elif grid[y][x] == 1 and (neighbors < 2 or neighbors > 3):
                grid[y][x] = 0

            #paint
            if grid[y][x] == 1:
                pygame.draw.rect(SCREEN, WHITE, (SQUARESTEPX * x, SQUARESTEPY * y, SQUARESTEPX, SQUARESTEPY))
            else:
                pygame.draw.rect(SCREEN, SCREEN_COLOR , (SQUARESTEPX * x, SQUARESTEPY * y, SQUARESTEPX, SQUARESTEPY))


    pygame.display.flip()
    time.sleep(0.1)

pygame.quit()