I am currently building a small web application to improve my skills, as part of this, I am trying to go with best practices across the board, testing, CI, well architected, clean code, all of that. Over the last few sessions of working on it, I have been struggling with a test on my root route where instead of returning a string via the route function, I am rendering a template, I have gotten it to work, but I don't understanding why it works, and this bothers me.
Primarily, it's the use of the b, before my assertion string, I assume it is to do with the fact that what I am rendering is not a string, but a html representation, akin to the difference between return and print, but I am hazy and would appreciate for someone to school me.
The line I am asking about is line 4 of the test_homepage_response function. And how it operates. Especially in regards to this error I was getting:
ERROR: test_home_welcome_return (tests.home_page_tests.HomePageTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/xibalba/code/reel/tests/home_page_tests.py", line 31, in test_home_welcome_return
self.assertIn(u"Welcome to Reel!", response.data)
File "/usr/local/lib/python3.6/unittest/case.py", line 1077, in assertIn
if member not in container:
TypeError: a bytes-like object is required, not 'str'
# Test Suite
import unittest
from reel import app
from reel.views import home_welcome
class HomePageTesttClass(unittest.TestCase):
@classmethod
def setupClass(cls):
pass
@classmethod
def tearDownClass(cls):
pass
def setUp(self):
self.app = app.test_client()
self.app.testing = True
def test_homepage_response(self):
result = self.app.get('/')
self.assertEqual(result.status_code, 200)
self.assertIn(b"Welcome to Reel!", result.data)
def tearDown(self):
pass
if __name__ == '__main__':
unittest.main()
from reel import app
from flask import render_template
@app.route('/')
def home_welcome():
return render_template("homepage.html")
@app.route('/signup')
def signup_welcome():
return 'Welcome to the signup page!'
@app.route('/login')
def login_welcome():
return 'Welcome to the login page!'
@app.route('/become_a_guide')
def guide_welcome():
return 'Welcome to the guide page!'
@app.route('/help')
def help_welcome():
return 'Welcome to the help page!'
Some of the resources I used figuring this out, which pointed me towards the use of the b:
https://github.com/mjhea0/flaskr-tdd#first-test
What does the 'b' character do in front of a string literal?
Appreciate this is a long one, I tried to provide as much context because I honestly feel pretty stupid with this question, but I didn't want to just continue on without knowing why the solution I'm using is working.
Thank you as always.
The very short simple answer, is that string belongs to the str
type, while "b" in front of a string will now make it a bytes object, belonging to type
bytes. Therefore, the expectation is that yes they should in fact not equal to each other because of the comparison of different types will be expected to fail.
Furthermore, the assertion you are using assertIn
, is using the in
keyword to test. In order to properly test with in
, you need to compare bytes to bytes in this case.
Observe this simple example, that takes you through replicating what you are experiencing:
>>> s = "this is a string"
>>> t = "this is another string"
>>> type(s) == type(t)
True
>>> sb = b"this is a string"
>>> type(sb) == type(s)
False
>>> s in sb
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'
So, as you can see, the "b" actually serves a functional purpose here that gives you a different type of "object".
You could also decode to a string:
>>> res = sb.decode()
>>> type(res)
<class 'str'>
Suggest being explicit about your decoding, however:
>>> res = sb.decode('utf-8')
>>> type(res)
<class 'str'>
Finally, here is an excellent more detailed explanation about the containment test with bytes
. Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With