【问题标题】:How to unit test a form submission when multiple forms on a route?当路由上有多个表单时,如何对表单提交进行单元测试?
【发布时间】:2015-11-24 06:47:21
【问题描述】:

我想向我的烧瓶应用程序添加单元测试,以测试有效和无效登录 + 注册时的表单行为。目前,我在一个页面和路由上托管了注册表单和登录表单,并且正在使用隐藏的输入字段来识别提交/确定下一步操作的两个表单中的哪一个。

我的问题是 - 如何编写针对页面上特定表单的单元测试?到目前为止,我看到的所有示例都将数据发布到特定路线,这就是我目前正在做的事情。但这失败了,因为我需要一种额外的方式来表达“我们正在提交 x 表单”。

那么有没有办法在 post 请求中添加“我们正在提交 x 表单”?

** 编辑添加,这是我尝试在发布数据字典中传递隐藏表单数据的不同方式,但没有成功。

data = dict(username="test@gmail.com", password="test", login_form) 
data = dict(username="test@gmail.com", password="test", "login_form")
data = dict(username="test@gmail.com", password="test", login_form=True)

登录单元测试:

from app import app
import unittest

class FlaskTestCase(unittest.TestCase):

  #ensure that login works with correct credentials
  def test_correct_login(self):
    tester = app.test_client(self)
    response = tester.post(
      '/login',
      data = dict(username="test@gmail.com", password="test"),
      follow_redirects=True
      )
    self.assertIn(b'you are logged in', response.data)

views.py 中的登录路径:

@app.route('/login', methods=['POST', 'GET'])
def login():
  login_form = LoginForm()
  signup_form = SignupForm()
  error_login = ''
  error_signup = ''

  #login form
  if 'login_form' in request.form and login_form.validate():
   # do login form stuff

  #signup form 
  if 'signup_form' in request.form and signup_form.validate():
   # do signup form stuff

  return render_template('login.html', login_form=login_form, signup_form=signup_form, error=error)

login.html:

<div class="login-form form-400">
      <h3>Log In To Your Account</h3>
      <form action="" method="post">
      <input type="hidden" name="login_form">
      {% if error_login != '' %}
      <label class="error">
        {{ error_login }}
      </label>
      {% endif %}

      {% from "_formhelper.html" import render_field %}
      {{ login_form.hidden_tag() }}
      {{ render_field(login_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
      {{ render_field(login_form.password, placeholder="Your Password", class="form-item__full") }}
      <input type="submit" value="Login" class="button button-blue">
    </form>
  </div>

  <p class="login-divider">or</p>

  <div class="signup-form form-400">
    <h3>Create a New Account</h3>
    <form action="" method="post">
      <input type="hidden" name="signup_form">
      {% if error_signup != '' %}
      <label class="error">
        {{ error_signup | safe}}
      </label>
      {% endif %}

      {% from "_formhelper.html" import render_field %}
      {{ signup_form.hidden_tag() }}
      {{ render_field(signup_form.username, placeholder="Pick a Username", class="form-item__full") }}
      {{ render_field(signup_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
      {{ render_field(signup_form.password, placeholder="Create a Password", class="form-item__full") }}

      <input type="submit" value="Sign Up" class="button button-green">
    </form>
  </div>

【问题讨论】:

  • 您需要做两件事之一。您需要在表单中包含具有唯一名称或值的输入,或者按照我推荐的方式,每个表单都应该发布到自己的端点。

标签: python html forms unit-testing flask


【解决方案1】:

更新: 感谢您用您已经尝试过的内容更新您的问题!关于导致问题的原因,我还有其他一些想法。

让我们继续查看 HTML 并尝试了解您的程序所基于的技术。在服务器端 login.html 文件中,请注意以下几行:

      {% from "_formhelper.html" import render_field %}
      {{ login_form.hidden_tag() }}
      {{ render_field(login_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
      {{ render_field(login_form.password, placeholder="Your Password", class="form-item__full") }}

它不是 HTML,可能正在服务器端进行处理以生成 HTML 并提供给客户端。包含login_form.hidden_tag() 的行看起来很有趣,因此我建议在浏览器中加载此页面并检查提供给客户端的 HTML。不幸的是,我之前没有使用过 Flask,所以我无法提供任何更直接的帮助。

但是,我的建议是继续深入研究 Flask 和 HTML 表单的工作原理。 Python 的好处是您可以访问库的源代码,这使您可以了解它们的工作原理,以便了解如何使用它们并修复使用它们的应用程序中的错误。

对不起,我不能给你更直接的帮助,祝你好运!


让我们看看 login.html。提交表单时,views.py 中的登录路由如何知道提交了哪个表单?如果您知道 HTML 表单,那么嵌套在表单中的 &lt;input&gt; 元素用于在这种情况下将数据发布到您的服务器/应用程序。

回到 login.html,注意这两行:

...
      <h3>Log In To Your Account</h3>
      <input type="hidden" name="login_form">
...
    <h3>Create a New Account</h3>
    <form action="" method="post">
      <input type="hidden" name="signup_form">
...

这些是&lt;input&gt; 元素,具有“隐藏”类型,因此不会显示,名称为“login_form”和“signup_form”,它们包含在表单提交的数据中。

现在在 views.py 的登录路径中,您会注意到有两行:

  #login form
  if 'login_form' in request.form and login_form.validate():
   # do login form stuff

  #signup form 
  if 'signup_form' in request.form and signup_form.validate():
   # do signup form stuff

他们正在测试列表request.form 中是否存在短语“login_form”或“signup_form”。现在回到你的单元测试:

    response = tester.post(
      '/login',
      data = dict(username="test@gmail.com", password="test"),
      follow_redirects=True
      )

注意您在dict 中传递的数据,这是在模仿表单数据,因此您可能应该包含“login_form”或“signup_form”以正确模仿 HTML 表单的行为。

如果您不熟悉 HTML 表单和 HTTP Post,我建议您搜索一些教程,或者只是阅读 MDN 或其他地方的文档。在基于某种技术(如 HTTP 和 HTML)构建软件时,当您在自己的软件中遇到错误时,了解这些技术是如何工作的会很有帮助。

希望这会有所帮助,如果我能澄清任何事情,请告诉我!

【讨论】:

  • 感谢您的回复。我的问题是如何在字典中传递“login_form”数据。我尝试了一些方法,包括 data = dict(username="test@gmail.com", password="test", login_form)data = dict(username="test@gmail.com", password="test", "login_form")data = dict(username="test@gmail.com", password="test", login_form=True) 都没有成功。我将使用此信息更新我的问题。
  • @SeeEmBee 感谢您的反馈,我用更多建议更新了我的答案。抱歉,我不能再提供帮助了!
【解决方案2】:

好的,我想通了。要传递 login_form 信息,我必须最终在 login_form 上传递一个空字符串,如下所示:

def test_correct_login(self):
    tester = app.test_client(self)
    response = tester.post(
      '/login',
      data = dict(username="test@gmail.com", password="test", login_form=""),
      follow_redirects=True
      )
    self.assertIn(b'you are logged in', response.data)

我通过在我的views.py 中为这条路线抛出一个print request.form 来做到这一点,然后看到了空字符串。

它仍然失败,但是由于 WTForms 模块添加的 csrf 令牌,login_form.validate() 失败了。最终,这场讨论有了答案。 Flask-WTF / WTForms with Unittest fails validation, but works without Unittest

感谢@drdrez 的建议!

【讨论】:

    【解决方案3】:

    您可能会遇到问题,因为您没有将请求标记为表单应用程序内容类型。我注意到您正在尝试访问 request.form,这需要以某种方式解析数据包。您可以尝试执行以下操作:

        response = tester.post(
      '/login',
      data = dict(username="test@gmail.com", password="test"),
      follow_redirects=True, 
      headers = {"Content-Type":"application/x-www-form-urlencoded"}
      )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-19
      • 2014-09-21
      • 2014-06-28
      • 2016-05-16
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      相关资源
      最近更新 更多