当我运行包括使用setuptools和nose2调用@classmethod的测试时,测试套件没有完成它,只是继续运行。然而,我已经检查了测试确实通过并到达了函数的末尾,测试套件只是没有完成运行。如果我使用decode_auth_token删除测试,它会工作得很好。我能够将其范围缩小到类方法,因为我也测试了其他类方法,它们也会导致相同的问题。
有人知道为什么会发生这种情况吗?下面是相关的代码片段,我的代码不会太多
我的用户模型中的代码
@classmethod
def decode_auth_token(cls, auth_token):
try:
payload = jwt.decode(auth_token, config.SECRET_KEY, algorithms=['HS256'])
# check the hash of what we expect the token to be and token we got to be the same
if bcrypt.check_password_hash(User.by_id(payload['sub']).api_token_hash, auth_token):
return payload['sub']
else:
return 'Token does not match Api Token.'
except jwt.ExpiredSignatureError:
return 'Signature expired. Please log in again.'
except jwt.InvalidTokenError:
return 'Invalid Token. Please log in again.'以下两个函数在调用时也会导致问题
@classmethod
def is_username_taken(cls, username):
return db.session.query(db.exists().where(User.username==username)).scalar()
@classmethod
def is_email_taken(cls, email):
return db.session.query(db.exists().where(User.email==email)).scalar()不过,此函数在调用时不会导致问题
@classmethod
def by_username(cls, username):
return User.query.filter(User.username == username).first()以下是测试结果
import unittest
import sys
from . import AppTestCase, API_ROOT
from app.extensions import db, bcrypt
from app.models import User, UserSchema, Location, Company
class TestUserModel(AppTestCase):
def test_encode_auth_token(self):
user = User.by_username('jdoe')
auth_token = user.encode_auth_token(user.id)
self.assertTrue(isinstance(auth_token, bytes))
def test_decode_auth_token(self):
user = User.by_username('jdoe')
auth_token = user.encode_auth_token(user.id)
self.assertTrue(isinstance(auth_token, bytes))
self.assertEqual(User.decode_auth_token(auth_token), user.id)
print('DONE')第一个测试运行良好,第二个测试打印出Done并正确解码返回正确用户id的auth_token,但不会导致测试套件完成。它只是在打印完成后继续运行。
下面是设置脚本,我使用python setup.py test运行测试
import os
from setuptools import setup, find_packages, Command
# Thanks http://stackoverflow.com/questions/3779915/why-does-python-setup-py-sdist-create-unwanted-project-egg-info-in-project-r
class CleanCommand(Command):
"""Custom clean command to tidy up the project root."""
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
os.system('rm -vrf ./build ./dist ./*.pyc ./*.tgz ./*.egg-info')
with open('requirements.txt') as f:
requirements = f.read().splitlines()
setup(
name="XXX",
description="XXX",
version=1.0,
packages=find_packages(),
install_requires=requirements,
include_package_data=True,
test_suite='nose2.collector.collector',
tests_require=['nose2'],
cmdclass={
'clean': CleanCommand,
}
)运行且不停止时的输出
running test
Searching for nose2
Best match: nose2 0.6.5
Processing nose2-0.6.5-py3.6.egg
Using XXX/.eggs/nose2-0.6.5-py3.6.egg
running egg_info
writing doomfist.egg-info/PKG-INFO
writing dependency_links to XXX.egg-info/dependency_links.txt
writing requirements to XXX.egg-info/requires.txt
writing top-level names to XXX.egg-info/top_level.txt
reading manifest file 'XXX.egg-info/SOURCES.txt'
writing manifest file 'XXX.egg-info/SOURCES.txt'
running build_ext
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/parser.py:50: DeprecationWarning: invalid escape sequence \.
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/parser.py:50: DeprecationWarning: invalid escape sequence \.
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:197: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:247: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:197: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:247: DeprecationWarning: invalid escape sequence \{
NOT running in debug mode
DONE
^]^\[1] 35752 quit python setup.py test编辑-很抱歉这篇很大的帖子,在评论中有一些人的建议,我使用调试器来确定它确实完成了测试。它真正被卡住的地方是在tearDown()过程中。我的以下函数就是它被卡住的地方。
def tearDown(self):
"""Clean db session and drop all tables."""
db.drop_all()随着调试器的进一步深入,我确定它最终会卡住
for table, fkcs in collection:
if table is not None:
self.traverse_single(table, drop_ok=True, _is_metadata_operation=True)
else:
for fkc in fkcs:
...更具体地说,关于这个方法self.traverse_single(table, drop_ok=True, _is_metadata_operation=True)。我猜它是在等待发电机返回时卡住了吧?不确定,但下面是我在它再次卡住之前得到的最后几行代码。
> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/ddl.py(929)visit_table()->None
-> _is_metadata_operation=_is_metadata_operation)
(Pdb) n
--Call--
> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/visitors.py(150)_visitor_iterator()-><sqlalchemy.s...t 0x112045630>
-> yield v
(Pdb) n
GeneratorExit
> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/visitors.py(150)_visitor_iterator()-><sqlalchemy.s...t 0x112045630>
-> yield v
(Pdb) l
145 def _visitor_iterator(self):
146 """iterate through this visitor and each 'chained' visitor."""
147
148 v = self
149 while v:
150 -> yield v
151 v = getattr(v, '_next', None)
152
153 def chain(self, visitor):
154 """'chain' an additional ClauseVisitor onto this ClauseVisitor.
155
(Pdb) n我想它卡在我下面的桌子上了
from ..helpers import get_current_time
from ..extensions import db, ma
from ..constants import STRING_LEN, DESCRIPTION_LEN
from .worker import WorkerSchema
class Injury(db.Model):
__tablename__ = "injuries"
def __repr__(self):
return '<Injury %r>' % (self.id)
id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.String(STRING_LEN), nullable=False)
description = db.Column(db.String(DESCRIPTION_LEN), nullable=False)
worker_id = db.Column(db.Integer, db.ForeignKey('workers.id'))
created_at = db.Column(db.DateTime, nullable=False, default = get_current_time)
updated_at = db.Column(db.DateTime, nullable=False, default = get_current_time, onupdate=get_current_time)
# Relationships
worker = db.relationship('Worker', back_populates='injuries')
# ================================================================
# ================================================================
# methods
# ================================================================
# Class methods
@classmethod
def by_id(cls, id):
return cls.query.filter(Injury.id==id).first()
class InjurySchema(ma.Schema):
class Meta:
fields = ('id', 'title', 'description', 'worker')
worker = ma.Nested(WorkerSchema)发布于 2017-07-28 01:23:23
基于这篇文章的SQLAlchemy blocked on dropping tables,我在我的drop_all命令前添加了db.session.close(),就能让它正常工作
def tearDown(self):
"""Clean db session and drop all tables."""
db.session.close()
db.drop_all()我仍然需要找出为什么会话是打开的,以及我需要在哪里关闭它
https://stackoverflow.com/questions/45260852
复制相似问题