是否可以在多行中打破长函数名称?

时间:2016-12-04 04:12:10

标签: python

我们的开发团队使用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()

我的选项:

  •   

    你可以写更短的方法名称!

    我知道,但我不想失去测试名称的描述性。

  •   

    您可以在每个测试之上编写多行注释,而不是使用长名称!

    这是一个不错的主意,但是当我在IDE(PyCharm)中运行测试时,我将无法看到测试名称。

  •   

    也许你可以用反斜杠(逻辑行继续符)继续行。

    不幸的是,这不是Python中的一个选项,正如Dan的回答中提到的那样。

  •   

    你可以停止测试。

    这在某些方面是有道理的,但鼓励格式良好的测试套件是很好的。

  •   

    您可以增加行长限制。

    我们的团队喜欢有限制,因为它有助于在窄显示屏上保持代码可读,因此这不是最佳选项。

  •   

    您可以从方法的开头删除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()

或者我必须咬紧牙关并自己缩短它?

6 个答案:

答案 0 :(得分:74)

不,这是不可能的。

在大多数情况下,从功能的可读性和可用性的角度来看,这样一个长名称是不可取的,尽管您的测试名称用例看起来很合理。

lexical rules of Python不允许将单个标记(在本例中为标识符)分割为多行。逻辑行继续符(行尾的\)可以将多个物理行连接到一个逻辑行,但不能跨多行连接单个令牌

答案 1 :(得分:50)

还可以编写一个装饰器,为方法改变.__name__

def test_name(name):
    def wrapper(f):
        f.__name__ = name
        return f
    return wrapper

然后你可以写:

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()

依赖于Python连接源相邻字符串文字的事实。

答案 2 :(得分:33)

根据这个问题的答案:How to disable a pep8 error in a specific file?,使用# nopep8# noqa结尾注释来禁用长线PEP-8。知道什么时候打破规则很重要。当然,Python的Zen会告诉你“特殊情况不足以破坏规则。”

答案 3 :(得分:7)

针对问题的特定于上下文的方法的排序。您提供的测试用例实际上看起来非常像自然语言格式,用于描述测试用例的必要步骤。

在这里查看使用behave Behavior Driver development style framework是否更有意义。您的“功能”可能如下(请参阅givenwhenthen如何反映您的内容:

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

还有相关主题的最新答案中的相关pyspecs package示例用法:

答案 4 :(得分:5)

较短的功能名称解决方案有很多优点。想想你的实际功能名称中已经真正的内容以及已经提供的内容。

test_that_client_event_listener_receives_connection_refused_error_without_server(self):

当你运行它时,你肯定知道它是一个测试吗?你真的需要使用下划线吗?是像“'”这样的词。真的需要理解这个名字吗?骆驼的情况会如同可读吗?下面的第一个例子如何重写上面的内容(字符数= 79):   接受使用缩写来获得一小部分常用词的惯例甚至更有效,例如: Connection = Conn,Error = Err。使用缩写时,您必须注意上下文,只有在不存在混淆时才使用它们 - 下面的第二个例子。   如果您接受实际上不需要在方法名称中提及客户端作为测试主题,因为该信息在类名中,那么第三个示例可能是合适的。 (54)字符。

ClientEventListenerReceivesConnectionRefusedErrorWithoutServer(个体):

ClientEventListenerReceivesConnRefusedErrWithoutServer(个体):

EventListenerReceiveConnRefusedErrWithoutServer(个体):

我也同意B Rad C&#34的建议;使用描述性名称作为msg kwarg arg in self.assert"您应该只对运行测试套件时失败测试的输出感兴趣。验证您已完成所有必要的测试不应该依赖于方法名称如此详细。

P.S。我可能也会删除“没有服务器”#39;也是多余的。如果服务器因任何原因无法联系,客户端事件处理程序是否应该接收事件? (虽然我认为如果客户端无法连接到服务器会收到某种“连接不可用”的情况会更好,但拒绝连接表明服务器可以是发现但拒绝连接本身。)

答案 5 :(得分:5)

对这种名字的需求可能暗示其他气味。

class ClientConnectionTest(unittest.TestCase):
   def test_that_client_event_listener_receives_connection_refused_error_without_server(self):
       ...

ClientConnectionTest听起来相当广泛(并且根本不像一个可测试的单位),并且很可能是一个大型课程,里面有大量的测试可以重新聚焦。像这样:

class ClientEventListenerTest(unittest.TestCase):
  def receives_connection_refused_without_server(self):
      ...

“测试”在名称中没用,因为它暗示着。

根据您提供的所有代码,我的最终建议是:重构您的测试代码,然后重新审视您的问题(如果它仍在那里)。