我们的开发团队使用PEP8 linter,最大行长为80个字符.
当我在python中编写单元测试时,我喜欢用描述性的方法名来描述每个测试的作用.然而,这通常会导致我超出角色限制.
这是一个太长的函数的例子......
class ClientConnectionTest(unittest.TestCase):
def test_that_client_event_listener_receives_connection_refused_error_without_server(self):
self.given_server_is_offline()
self.given_client_connection()
self.when_client_connection_starts()
self.then_client_receives_connection_refused_error()
Run Code Online (Sandbox Code Playgroud)
你可以写更短的方法名称!
我知道,但我不想失去测试名称的描述性.
您可以在每个测试之上编写多行注释,而不是使用长名称!
这是一个不错的主意,但是当我在IDE(PyCharm)中运行测试时,我将无法看到测试名称.
也许你可以用反斜杠(逻辑行继续符)继续行.
不幸的是,这不是Python中的一个选项,正如Dan的回答中提到的那样.
你可以停止linting你的测试.
这在某些方面是有道理的,但鼓励格式良好的测试套件是很好的.
您可以增加行长度限制.
我们的团队喜欢有限制,因为它有助于在窄显示屏上保持代码可读,因此这不是最佳选择.
您可以
test从方法的开头删除.
这不是一个选择.Python的测试运行器需要所有测试方法开始,test否则它们不会接收它们.
编辑:一些测试运行器允许您在搜索测试函数时指定正则表达式,但我不想这样做,因为它是为项目所有人工作的额外设置.此外,它并没有真正回答原来的问题.
您可以将EventListener分隔为自己的类并单独测试它.
事件监听器属于自己的类(并经过测试).它只是一个由ClientConnection中发生的事件触发的接口.这种建议似乎有很好的意图,但是被误导,并没有帮助回答原始问题.
您可以使用像Behave这样的BDD框架.它专为表达测试而设计.
这是事实,我希望将来可以使用更多这些.虽然我仍然想知道如何跨行分割函数名称.
Python中是否有一种方法可以跨多行拆分长函数声明?
例如...
def test_that_client_event_listener_receives_
connection_refused_error_without_server(self):
self.given_server_is_offline()
self.given_client_connection()
self.when_client_connection_starts()
self.then_client_receives_connection_refused_error()
Run Code Online (Sandbox Code Playgroud)
或者我必须自己咬紧牙关并缩短它?
Dan*_*ski 75
不,这是不可能的.
在大多数情况下,从功能的可读性和可用性的角度来看,这样一个长名称是不可取的,尽管您的测试名称用例似乎很合理.
Python的词法规则不允许将单个标记(在本例中为标识符)分割为多行.逻辑行延续字符(\在行的末尾)可以将多个物理行连接到单个逻辑行,但不能跨多行连接单个标记.
Sea*_*ira 50
你可能也写的是一个变异装饰.__name__的方法.
def test_name(name):
def wrapper(f):
f.__name__ = name
return f
return wrapper
Run Code Online (Sandbox Code Playgroud)
然后你可以写:
class ClientConnectionTest(unittest.TestCase):
@test_name("test_that_client_event_listener_"
"receives_connection_refused_error_without_server")
def test_client_offline_behavior(self):
self.given_server_is_offline()
self.given_client_connection()
self.when_client_connection_starts()
self.then_client_receives_connection_refused_error()
Run Code Online (Sandbox Code Playgroud)
依赖于Python连接源相邻字符串文字的事实.
mat*_*mc3 33
根据这个问题的答案:如何禁用特定文件中的pep8错误?,使用# nopep8或# noqa尾随注释禁用长线PEP-8.知道什么时候打破规则很重要.当然,Python的Zen会告诉你"特殊情况不足以打破规则."
我们可以将decorator应用于类而不是方法,因为unittestget方法的名称来自dir(class).
装饰器decorate_method将遍历类方法并根据func_mapping字典重命名方法的名称.
在看到来自@Sean Vieira的装饰回答之后想到这一点,来自我的+1
import unittest, inspect
# dictionary map short to long function names
func_mapping = {}
func_mapping['test_client'] = ("test_that_client_event_listener_receives_"
"connection_refused_error_without_server")
# continue added more funtion name mapping to the dict
def decorate_method(func_map, prefix='test_'):
def decorate_class(cls):
for (name, m) in inspect.getmembers(cls, inspect.ismethod):
if name in func_map and name.startswith(prefix):
setattr(cls, func_map.get(name), m) # set func name with new name from mapping dict
delattr(cls, name) # delete the original short name class attribute
return cls
return decorate_class
@decorate_method(func_mapping)
class ClientConnectionTest(unittest.TestCase):
def test_client(self):
# dummy print for testing
print('i am test_client')
# self.given_server_is_offline()
# self.given_client_connection()
# self.when_client_connection_starts()
# self.then_client_receives_connection_refused_error()
Run Code Online (Sandbox Code Playgroud)
测试运行unittest如下所示确实显示了完整的长描述性函数名称,认为它可能适用于您的情况虽然它可能听起来不那么优雅和可读的实现
>>> unittest.main(verbosity=2)
test_that_client_event_listener_receives_connection_refused_error_without_server (__main__.ClientConnectionTest) ... i am client_test
ok
Run Code Online (Sandbox Code Playgroud)
针对问题的特定于上下文的方法.您提供的测试用例实际上看起来非常像自然语言格式,用于描述测试用例的必要步骤.
看看使用behave行为驱动程序开发风格框架是否会更有意义.您的"特征"可能看起来像(怎么看given,when,then反映你有什么):
Feature: Connect error testing
Scenario: Client event listener receives connection refused error without server
Given server is offline
when client connect starts
then client receives connection refused error
Run Code Online (Sandbox Code Playgroud)
还有相关主题的相关pyspecs包,样本用法来自最近的答案:
较短的功能名称解决方案有很多优点.想想实际功能名称中真正需要的是什么以及已经提供了什么.
test_that_client_event_listener_receives_connection_refused_error_without_server(self):
Run Code Online (Sandbox Code Playgroud)
当你运行它时,你肯定知道这是一个测试吗?你真的需要使用下划线吗?这个名字真的需要用'那个'来理解?骆驼的情况会如同可读吗?下面的第一个例子如何重写上面的内容(字符数= 79):接受使用缩写来处理一小部分常用字的约定更有效,例如Connection = Conn,Error = Err.使用缩写时,您必须注意上下文,只有在不存在混淆时才使用它们 - 下面的第二个例子.如果您接受实际上不需要在方法名称中提及客户端作为测试主题,因为该信息在类名中,那么第三个示例可能是合适的.(54)字符.
ClientEventListenerReceivesConnectionRefusedErrorWithoutServer(个体经营):
ClientEventListenerReceivesConnRefusedErrWithoutServer(个体经营):
EventListenerReceiveConnRefusedErrWithoutServer(个体经营):
我也同意B Rad C的建议"在self.assert中使用描述性名称作为msg kwarg arg"你应该只对在运行测试套件时看到失败测试的输出感兴趣.验证您已经编写了所有必要的测试,不应该依赖于方法名称如此详细.
PS我也可能删除'WithoutServer'也是多余的.如果服务器因任何原因无法联系,客户端事件处理程序是否应该接收该事件?(虽然tbh我认为如果客户端无法连接到服务器它会收到某种"连接不可用"会更好,但拒绝连接表明可以找到服务器但拒绝连接本身.)
对这种名称的需求可能会暗示其他气味.
class ClientConnectionTest(unittest.TestCase):
def test_that_client_event_listener_receives_connection_refused_error_without_server(self):
...
Run Code Online (Sandbox Code Playgroud)
ClientConnectionTest听起来相当广泛(并且根本不像一个可测试的单位),并且可能是一个大型的课程,内部有大量的测试可以重新聚焦.像这样:
class ClientEventListenerTest(unittest.TestCase):
def receives_connection_refused_without_server(self):
...
Run Code Online (Sandbox Code Playgroud)
"测试"在名称中没用,因为它是隐含的.
有了你给我的所有代码,我的最终建议是:重构你的测试代码,然后重新审视你的问题(如果它还在那里).