How can I test a post request to a wtform that uses FormField in flask?

13 Views Asked by At

I'm trying to write tests to see if a form is posting data properly. Specifically I'm trying to test that form data (name and phone_number) are being sent to a contact info form for takeout. I keep running into an error that I think is due to the fact I'm using FormField and I'm not sure what the correct data to send is.

I'm using python unittest for a Flask application I'm writing that is supposed to be a mock Order Management System.

Here's the test:

test_routes.py

import os
import models
from flask import get_flashed_messages, session
from unittest import TestCase

# Before importing app, set environmental variable to use a test db for tests
os.environ['DATABASE_URL'] = 'postgresql:///omakase-test'

# Now import app
from app import app
app.config['WTF_CSRF_ENABLED'] = False

# Make Flask errors be real errors, not HTML pages with error info
app.config['TESTING'] = True

app.config['DEBUG_TB_HOSTS'] = ['dont-show-debug-toolbar']

def test_post_takeout_contact_form(self):
with self.client:
resp = self.client.post('/takeout/contact-form', follow_redirects=True,
data={
'name': 'test customer',
'phone_number': '123-456-7890'
})
html = resp.get_data(as_text=True)
testUser = models.User.query.filter_by(name="test").first()

            self.assertEqual(resp.status_code, 200)
            self.assertIn("We're pleased to take your order", html)

I'm specifically trying to test and see if a flash message pops up, but I keep getting the error message:

======================================================================
FAIL: test_post_takeout_contact_form (tests.test_routes.OrderTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "~/projects/capstone-project-one/tests/test_routes.py", line 272, in test_post_takeout_contact_form
    self.assertIn("We're pleased to take your order", html)
AssertionError: "We're pleased to take your order" not found in '<!DOCTYPE html>\n<html 
lang="en">\n<head>\n    <meta charset="UTF-8">\n    <meta name="viewport" content="width=device-
width, initial-scale=1.0">\n    <title></title>\n    <link rel="stylesheet" 
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/united/bootstrap.min.css">\n    <link 
rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" 
integrity="sha512-
DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" 
crossorigin="anonymous" referrerpolicy="no-referrer" />\n</head>\n<body class="bg-light">\n\n<nav 
class="navbar navbar-expand-lg bg-danger" data-bs-theme="dark">\n  <div class="container-fluid">\n    
<a class="navbar-brand" href="/">Omakase OMS</a>\n    <button class="navbar-toggler" type="button" 
data-bs-toggle="collapse" data-bs-target="#navbarColor01" aria-controls="navbarColor01" aria-
expanded="false" aria-label="Toggle navigation">\n      <span class="navbar-toggler-icon"></span>\n    
</button>\n    <div class="collapse navbar-collapse" id="navbarColor01">\n      <ul class="navbar-
nav me-auto">\n        <li class="nav-item">\n          <a class="nav-link" href="/">Home\n            
<span class="visually-hidden">(current)</span>\n          </a>\n        </li>\n        <li 
class="nav-item">\n          <a class="nav-link" href="/dine-in/select-table">Dining In</a>\n        
</li>\n        <li class="nav-item">\n          <a class="nav-link" href="/takeout">Takeout</a>\n        
</li>\n        <li class="nav-item">\n          <a class="nav-link" href="/delivery">Delivery</a>\n        
</li>\n        <li class="nav-item dropdown">\n          <a class="nav-link dropdown-toggle" 
href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">\n            
Employees\n          </a>\n          <ul class="dropdown-menu" aria-labelledby="navbarDropdown">\n            
<li><a class="dropdown-item" href="/employee-dashboard">Employee Dashboard</a></li>\n            
<li><a class="dropdown-item" href="/kitchen-dashboard">Kitchen Dashboard</a></li>\n
 <li><a class="dropdown-item" href="/add-menu-item">Add Menu Item</a></li>\n            <li><hr 
class="dropdown-divider"></li>\n            <li><a class="dropdown-item" href="/employees/list"> 
Employee List</a></li>\n            <li><a class="dropdown-item" href="/employees/add-employee">Add 
Employee</a></li>\n          </ul>\n        </li>\n      </ul>\n      <ul class="navbar-nav me-1" 
id="user-profile">\n      \n        <li class="nav-item float-end me-2">\n          <a class="nav-
link" href="/login">Login</a>\n        </li>\n      \n      \n      </ul>\n      \n    </div>\n  
</div>\n</nav>\n\n\n  \n\n\n<div class="container-fluid g-0 p-0" style="min-height:75vh;">\n\n\n\n    
\n    <div class="container p-3 bg-secondary mb-5 rounded">\n        <h1>Please Fill Out The 
Information Below</h1>\n        \n        \n<div class="container p-3 bg-light">\n<h3 
class="display-3">Takeout</h3>\n\n    <form id="takeout-form" method="POST">\n     <!--add 
type=hidden form fields -->\n\n    \n\n        \n        \n\n        <label 
for="contact_info">Contact Info</label>\n        <div class="container bg-white rounded py-2">\n            
\n            <label for="contact_info-name">Name</label>\n            <input class="form-control 
my-3" id="contact_info-name" name="contact_info-name" required type="text" value="">\n            
\n            <label for="contact_info-phone_number">Phone Number</label>\n            <input 
class="form-control my-3" id="contact_info-phone_number" name="contact_info-phone_number" 
type="text" value="">\n            \n        </div>\n        \n        \n\n        <p>\n        \n        
<small class="form-text text-danger">\n            name\n        </small>\n        \n        
</p>\n\n    \n\n    <button class="btn btn-success btn-lg" type="submit">Submit</button>\n    
</form>\n</div>\n\n    </div>\n\n\n</div>\n\n<footer>\n<div class="container-fluid text-center bg-
secondary d-flex justify-content-center" style="height:100px;">\n  \n  <div class="row">\n\n    
<div class="col align-self-center">\n    Vegetarian image by <a href="https://www.freepik.com/free-
vector/flat-world-vegetarian-day-labels-
collection_30590488.htm#query=vegetarian&position=13&from_view=search&track=sph&uuid=592ca90d-bf6b-
4116-9533-47be0d368220">Freepik</a>\n    </div>\n\n  </div>\n\n</div>\n</footer>\n    \n<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" 
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" 
crossorigin="anonymous"></script>   \n\n\n</body>\n</html>'

----------------------------------------------------------------------
Ran 5 tests in 0.227s

FAILED (failures=1)

Checking the code in the html page, I noticed that the form is not accepting the data, and so it's just redirecting to the form again.

After examining the form data using flask toolbar, I noticed that the form data being sent is form.contact_info.data, which is an object of name and phone_number. So I thought sending:

data={
    "contact_info": {
        "name": "test",
        "phone_number": "123-456-7890"
        }
    }

in testing would work, but I keep getting the same error as above. I'm not really sure what else to try.

I'm out of ideas of what to do next because I can't figure out why the form data isn't being sent in the test file. I'd really appreciate some advice or direction to what I could look up to get a better understanding of what I'm doing wrong.

I'll include the relevant code sections from the form.py and routes below.

app.py

@app.route('/<state>/contact-form', methods=['GET', 'POST'])
def contact_form(state):

    if state=='takeout':
        form = TakeoutForm()
        
        if form.validate_on_submit():
            name = form.contact_info.data['name']
            phone_number = form.contact_info.data['phone_number']
            customer = User(name=name, phone_number=phone_number, temp=True, 
            groups=[Group.query.filter_by(name='customer').first()])
            new_order = Order(type='Takeout')
            db.session.add_all([customer, new_order])
            db.session.commit()
            
            session['current_order_id'] = new_order.id
            session['temp_customer_id'] = customer.id

            flash(f"We're pleased to take your order, {name}!", 'success')
            return redirect(url_for('order_page'))

    if state=='delivery':
        form = DeliveryForm()
        
        if form.validate_on_submit():
            name = form.contact_info.data['name']
            phone_number = form.contact_info.data['phone_number']
            proto_add = [i.strip() for i in form.address.data.values()]
            address = ", ".join(proto_add)

            customer = User(name=name, phone_number=phone_number, address=address, temp=True, 
            groups=[Group.query.filter_by(name='customer').first()])
            new_order = Order(type='Takeout')
            db.session.add_all([customer, new_order])
            db.session.commit()
            
            session['current_order_id'] = new_order.id
            session['temp_customer_id'] = customer.id

            flash(f"We're pleased to take your order, {name}!", 'success')
            return redirect(url_for('order_page'))

        
    return render_template('contact-form.html', form=form, state=state)

forms.py

class ContactInfo(Form):
    """Subform for adding customer contact info"""

    name = StringField("Name", validators=[DataRequired()])
    phone_number = StringField("Phone Number")
    
class TakeoutForm(FlaskForm):
    contact_info = FormField(ContactInfo)
0

There are 0 best solutions below