希望大家能够帮帮我,已经被这个问题搞得彻夜难眠了 Orz
这是我的程序目录结构:
程序执行没有问题
但就是在执行单元测试 test.py 的时候报以下错误:
C:\Users\Administrator\AppData\Local\Programs\Python\Python39\python.exe "E:\PyCharm 2021.1.1\plugins\python\helpers\pycharm\_jb_unittest_runner.py" --target test.WatchList
Testing started at 9:21 ...
Launching unittests with arguments python -m unittest test.WatchList in E:\PycharmProjects\projects\my_flask
Failure
Traceback (most recent call last):
File "E:\PycharmProjects\projects\my_flask\test.py", line 40, in test_app_exist
self.assertIsNone(app)
AssertionError: <Flask 'watchlist'> is not None
Failure
Traceback (most recent call last):
File "E:\PycharmProjects\projects\my_flask\test.py", line 79, in test_createitem
self.assertNotIn('Item created.', data)
AssertionError: 'Item created.' unexpectedly found in '<!DOCTYPE html>\n<html lang="en">\n<head>\n \n <meta charset="utf-8">\n <!--添加了一个新的 <meta> 元素,这个元素会设置页面的视口,让页面根据设备的宽度来自动缩放页面,\n 这样会让移动设备拥有更好的浏览体验\n -->\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>Watchlist</title>\n <link rel="icon" href="/static/favicon.ico">\n <link rel="stylesheet" href="/static/style.css" type="text/css">\n \n</head>\n<body>\n<!-- 插入到页面标题上方 -->\n \n <div class="alert">Item created.</div>\n \n <h2>\n \n <img alt="Avatar" class="avatar" src="/static/images/avatar.png">\n Test\'s Watchlist\n \n </h2>\n <!--\n 添加了一个导航栏\n -->\n <nav>\n <ul>\n \n <li><a href="/">Home</a></li>\n <li><a href="/settings">Settings</a></li>\n <li><a href="/logout">Logout</a></li>\n\n </ul>\n </nav>\n \n<p>2 Titles</p>\n\n<form method="post">\n Name <input type="text" name="title" autocomplete="off" required>\n Year <input type="text" name="year" autocomplete="off" required>\n <input class="btn" type="submit" name="submit" value="Add">\n</form>\n\n<ul class="movie-list">\n \n <li>Test Movie Title - 2019\n <span class="float-right">\n <a class="imdb" href="https://movie.douban.com/subject_search?search_text=Test Movie Title" target="_blank" title="Find this movie on 豆瓣">search</a>\n </span>\n <span class="float-right">\n \n <a class="btn" href="/movie/edit/1">Edit</a>\n <form class="inline-form" method="post" action="/movie/delete/1">\n <input class="btn" type="submit" name="delete" value="Delete" onclick="return confirm(\'Are you sure?\')">\n </form>\n \n </span>\n\n\n </li>\n \n <li>New Movie - 2019\n <span class="float-right">\n <a class="imdb" href="https://movie.douban.com/subject_search?search_text=New Movie" target="_blank" title="Find this movie on 豆瓣">search</a>\n </span>\n <span class="float-right">\n \n <a class="btn" href="/movie/edit/2">Edit</a>\n <form class="inline-form" method="post" action="/movie/delete/2">\n <input class="btn" type="submit" name="delete" value="Delete" onclick="return confirm(\'Are you sure?\')">\n </form>\n \n </span>\n\n\n </li>\n \n</ul>\n<img alt="Walking Totoro" class="totoro" src="/static/images/totoro.gif" title="to~to~ro~">\n\n\n</body>\n</html>'
Failure
Traceback (most recent call last):
File "E:\PycharmProjects\projects\my_flask\test.py", line 58, in test_index_page
self.assertIn('Test\'s Watchlist', data)
AssertionError: "Test's Watchlist" not found in '<!DOCTYPE html>\n<html lang="en">\n<head>\n \n <meta charset="utf-8">\n <!--添加了一个新的 <meta> 元素,这个元素会设置页面的视口,让页面根据设备的宽度来自动缩放页面,\n 这样会让移动设备拥有更好的浏览体验\n -->\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>Watchlist</title>\n <link rel="icon" href="/static/favicon.ico">\n <link rel="stylesheet" href="/static/style.css" type="text/css">\n \n</head>\n<body>\n<!-- 插入到页面标题上方 -->\n \n <h2>\n \n <img alt="Avatar" class="avatar" src="/static/images/avatar.png">\n Admin\'s Watchlist\n \n </h2>\n <!--\n 添加了一个导航栏\n -->\n <nav>\n <ul>\n \n <li><a href="/login">Login</a></li>\n \n </ul>\n </nav>\n \n<p>1 Titles</p>\n\n<ul class="movie-list">\n \n <li>Test Movie Title - 2019\n <span class="float-right">\n <a class="imdb" href="https://movie.douban.com/subject_search?search_text=Test Movie Title" target="_blank" title="Find this movie on 豆瓣">search</a>\n </span>\n <span class="float-right">\n \n </span>\n\n\n </li>\n \n</ul>\n<img alt="Walking Totoro" class="totoro" src="/static/images/totoro.gif" title="to~to~ro~">\n\n\n</body>\n</html>'
Failure
Traceback (most recent call last):
File "E:\PycharmProjects\projects\my_flask\test.py", line 139, in test_login
self.assertIn('Login success.', data)
AssertionError: 'Login success.' not found in '<!DOCTYPE html>\n<html lang="en">\n<head>\n \n <meta charset="utf-8">\n <!--添加了一个新的 <meta> 元素,这个元素会设置页面的视口,让页面根据设备的宽度来自动缩放页面,\n 这样会让移动设备拥有更好的浏览体验\n -->\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>Watchlist</title>\n <link rel="icon" href="/static/favicon.ico">\n <link rel="stylesheet" href="/static/style.css" type="text/css">\n \n</head>\n<body>\n<!-- 插入到页面标题上方 -->\n \n <div class="alert">Login success</div>\n \n <h2>\n \n <img alt="Avatar" class="avatar" src="/static/images/avatar.png">\n Test\'s Watchlist\n \n </h2>\n <!--\n 添加了一个导航栏\n -->\n <nav>\n <ul>\n \n <li><a href="/">Home</a></li>\n <li><a href="/settings">Settings</a></li>\n <li><a href="/logout">Logout</a></li>\n\n </ul>\n </nav>\n \n<p>1 Titles</p>\n\n<form method="post">\n Name <input type="text" name="title" autocomplete="off" required>\n Year <input type="text" name="year" autocomplete="off" required>\n <input class="btn" type="submit" name="submit" value="Add">\n</form>\n\n<ul class="movie-list">\n \n <li>Test Movie Title - 2019\n <span class="float-right">\n <a class="imdb" href="https://movie.douban.com/subject_search?search_text=Test Movie Title" target="_blank" title="Find this movie on 豆瓣">search</a>\n </span>\n <span class="float-right">\n \n <a class="btn" href="/movie/edit/1">Edit</a>\n <form class="inline-form" method="post" action="/movie/delete/1">\n <input class="btn" type="submit" name="delete" value="Delete" onclick="return confirm(\'Are you sure?\')">\n </form>\n \n </span>\n\n\n </li>\n \n</ul>\n<img alt="Walking Totoro" class="totoro" src="/static/images/totoro.gif" title="to~to~ro~">\n\n\n</body>\n</html>'
Ran 15 tests in 2.988s
FAILED (failures=4)
Process finished with exit code 1
以下是我的单元测试的源码:
import unittest
from watchlist import app, db
from watchlist.models import Movie, User
from watchlist.commands import forge, dbinit
class WatchList(unittest.TestCase):
app.app_context().push() # 推送应用上下文环境
def setUp(self):
# 更新配置
app.config.update(
TESTING=True,
SQLALCHEMY_DATABASE_URI='sqlite:///:memory:'
)
# 创建数据库和表
db.create_all()
# 创建测试数据,一个用户一个电影条目
user = User(name='Test', username='test')
user.set_password('123')
movie = Movie(title='Test Movie Title', year='2019')
# 使用 add_all() 方法一次添加多个模型类实例,传入列表
db.session.add_all([user, movie])
db.session.commit()
self.client = app.test_client() # 创建测试客户端
self.runner = app.test_cli_runner() # 创建测试命令运行器
def tearDown(self):
db.session.remove() # 清除数据库会话
db.drop_all() # 删除数据库表
# 测试程序实例是否存在
def test_app_exist(self):
self.assertIsNone(app)
# 测试程序是否处于测试模式
def test_app_is_testing(self):
self.assertTrue(app.config['TESTING'])
# 测试 404 页面
def test_404page(self):
response = self.client.get('/errorpage') # 传入目标 URL
data = response.get_data(as_text=True)
self.assertIn('Page Not Found - 404', data)
self.assertIn('Go Back', data)
self.assertEqual(response.status_code, 404) # 判断响应状态码
# 测试主页
def test_index_page(self):
response = self.client.get('/')
data = response.get_data(as_text=True)
self.assertIn('Test\'s Watchlist', data)
self.assertIn('Test Movie Title', data)
self.assertEqual(response.status_code, 200)
# 辅助方法,用于登入用户
def login(self):
self.client.post('/login',
data=dict(username='test',
password='123'),
follow_redirects=True)
# 测试创建条目操作
def test_createitem(self):
self.login()
response = self.client.post('/', data=dict(title='New Movie', year='2019'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Item created.', data)
self.assertIn('New Movie', data)
# 电影标题为空情况
response = self.client.post('/', data=dict(title='', year='2019'), follow_redirects=True)
self.assertNotIn('Item created.', data)
self.assertIn('Invalid input.', data)
# 电影年份为空情况
response = self.client.post('/', data=dict(title='New Movie', year=''), follow_redirects=True)
self.assertNotIn('Item created.', data)
self.assertIn('Invalid input.', data)
# 更新条目操作
def test_updateitem(self):
self.login()
# 测试更新页面
response = self.client.get('/movie/edit/1')
data = response.get_data(as_text=True)
self.assertIn('Edit item', data)
self.assertIn('Test Movie Title', data)
self.assertIn('2019', data)
# 更新条目
response = self.client.post('/movie/edit/1', data=dict(title='New Movie Edited', year='2019'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Item updated.', data)
self.assertIn('New Movie Edited', data)
# 更新条目但电影标题为空
response = self.client.post('/movie/edit/1', data=dict(title='', year='2019'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Item updated.', data)
self.assertIn('Invalid input.', data)
# 更新条目但电影年份为空
response = self.client.post('/movie/edit/1', data=dict(title='New Movie Edited', year=''), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Item updated.', data)
self.assertNotIn('New Movie Edited Again', data)
self.assertIn('Invalid input.', data)
# 测试删除条目
def test_deleteitem(self):
self.login()
response = self.client.post('/movie/delete/1', follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Item deleted.', data)
self.assertNotIn('Test Movie Title', data)
# 测试登录保护
def test_login_protect(self):
response = self.client.get('/')
data = response.get_data(as_text=True)
self.assertNotIn('Logout', data)
self.assertNotIn('Settings', data)
self.assertNotIn('<form method="post">', data)
self.assertNotIn('Delete', data)
self.assertNotIn('Edit', data)
# 测试登录
def test_login(self):
response = self.client.post('/login',data=dict(username='test', password='123'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Login success.', data)
self.assertNotIn('Logout', data)
self.assertIn('Settings', data)
self.assertIn('Delete', data)
self.assertIn('Edit', data)
self.assertIn('<form method="post">', data)
# 测试使用错误的密码登录
response = self.client.post('/login',data=dict(username='test', password='456'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Login success.', data)
self.assertIn('Invalid username or password.', data)
# 测试使用错误的用户名登录
response = self.client.post('/login',data=dict(username='hehe', password='123'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Login success.', data)
self.assertIn('Invalid username or password.', data)
# 测试使用空用户名登录
response = self.client.post('/login',data=dict(username='', password='123'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Login success.', data)
self.assertIn('Invalid username or password.', data)
# 测试使用空密码登录
response = self.client.post('/login', data=dict(username='test', password=''), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Login success.', data)
self.assertIn('Invalid username or password.', data)
# 测试登出
def test_logout(self):
self.login()
response = self.client.get('/logout', follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Bye!', data)
self.assertNotIn('Logout', data)
self.assertNotIn('Settings', data)
self.assertNotIn('Delete', data)
self.assertNotIn('Edit', data)
self.assertNotIn('<form method="post">', data)
# 测试设置
def test_settingd(self):
self.login()
# 测试设置页面
response = self.client.get('/settings')
data = response.get_data(as_text=True)
self.assertIn('Settings', data)
self.assertIn('Your Name', data)
# 测试更新设置
response = self.client.post('/settings',data=dict(name='Edison'), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('Settings updated.', data)
self.assertIn('Edison', data)
# 测试更新设置,名称为空
response = self.client.post('/settings', data=dict(name=''), follow_redirects=True)
data = response.get_data(as_text=True)
self.assertNotIn('Settings updated.', data)
self.assertIn('Invalid input.', data)
# 测试虚拟数据增加
def test_forge(self):
result = self.runner.invoke(forge)
self.assertIn('Done.', result.output)
self.assertNotEqual(Movie.query.count(), 0)
# 测试初始化数据库
def test_dbinit(self):
result = self.runner.invoke(dbinit)
self.assertIn('Initialized database.', result.output)
# 测试生成管理员账户
def test_admin(self):
db.drop_all()
db.create_all()
result = self.runner.invoke(args=['admin', '--username', 'root', '--password', '123'])
self.assertIn('Creating user...', result.output)
self.assertIn('Done.', result.output)
self.assertEqual(User.query.count(), 1)
# 测试更新管理员账户
def test_admin_command_update(self):
# 使用 args 参数给出完整的命令参数列表
result = self.runner.invoke(args=['admin', '--username', 'peter', '--password', '456'])
self.assertIn('Updating user...', result.output)
self.assertIn('Done.', result.output)
self.assertEqual(User.query.count(), 1)
self.assertEqual(User.query.first().username, 'peter')
self.assertTrue(User.query.first().validate_password('456'))
if __name__ == '__main__':
unittest.main()