Is it possible to split a long function name into several lines?

Our development team uses the PEP8 linter, which requires a maximum line length of 80 characters .

When I write unit tests in python, I like to have descriptive method names to describe what each test does. However, this often leads me to exceed the limit of character.

Here is an example of a function too long ...

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

My parameters:

  • You can simply write shorter method names!

    I know, but I do not want to lose the descriptiveness of test names.

  • You can write multi-line comments on each test instead of using long names!

    This is a worthy idea, but then I will not be able to see the names of the tests when running the tests inside my IDE (PyCharm).

  • Perhaps you can continue the lines with a backslash (the continuation character of a logical line).

    Unfortunately, this is not an option in Python, as mentioned in Dan's answer.

  • You can stop the experiments.

    This makes sense in some ways, but it's nice to encourage a well-formatted test suite.

  • You can increase the string length limit.

    Our team likes to have a limit, because it helps to keep readable code on narrow displays, so this is not the best option.

  • You can remove test from the beginning of your methods.

    This is not an option. The Python test tester needs all the testing methods to start with test , or he won't pick them up.

    Edit: Some test runners allow you to specify a regular expression when searching for test functions, although I would prefer not to, because this is an additional setting for everyone working on the project. In addition, he does not answer the original question.

  • You can split the EventListener into your own class and test it separately.

    An event listener is in its class (and is being tested). This is just an interface that is triggered by events occurring in the ClientConnection. Such a proposal seems to have a good intention, but is misdirected and does not answer the original question.

  • You can use the BDD Framework, for example Behave . It is intended for rapid tests.

    This is true, and I hope to use them more in the future. Although I would still like to know how to separate function names by line.

In the end...

Is there a way in Python to split a declaration of a long function into several lines ?

For example...

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

Or will I have to bite a bullet and cut it myself?

+73
python
Dec 04 '16 at 4:12
source share
7 answers

No, It is Immpossible.

In most cases, such a long name would be undesirable in terms of readability and usability of the function, although your use case for test names seems pretty reasonable.

Python's lexical rules do not allow splitting one token (in this case, an identifier) ​​into several lines. The logical line continuation character ( \ at the end of the line) can join several physical lines in one logical line, but cannot join the same token along several lines.

+70
Dec 04 '16 at 4:33
source share

You can also write a decorator that mutates .__name__ for the method.

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

Then you could write:

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

relying on Python combining source adjacent string literals.

+48
Dec 04 '16 at 13:46 on
source share

In answer to this question: How to disable pep8 error in a specific file? , use the comment t20> or # noqa to disable PEP -8 for the long line. It is important to know when to break the rules. Of course, Zen Python would tell you that "Special cases are not complicated enough to break the rules."

+32
04 Dec '16 at 5:01
source share

We can apply decorator to the class instead of the method, since unittest get the method name from dir(class) .

The decorate_method decorator will go through the class methods and rename the method name based on the func_mapping dictionary.

Thinking about it after seeing the decorator answer from @Sean Vieira, +1 from me

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

test run with unittest , as shown below, shows the full long descriptive name of the function, thinks that this may work for your case, although it may not seem so elegant and readable from the implementation

 >>> unittest.main(verbosity=2) test_that_client_event_listener_receives_connection_refused_error_without_server (__main__.ClientConnectionTest) ... i am client_test ok 
+8
Dec 04 '16 at 8:05
source share

A view of the context-sensitive approach to the problem. The selected test case is really very similar to the Natural Language format, which describes the necessary steps for the test case.

See if the use of the behave behavior driver design style is more understandable here. Your “function” may look (see How given , when , then reflects what you had):

 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 

There is also a relevant pyspecs package , an example use from a recent answer on the topic:

  • Write Descriptive Unit Tests
+7
Dec 04 '16 at 5:33
source share

A shorter function name has many advantages. Think about what is really needed in your actual function name and what is already delivered.

 test_that_client_event_listener_receives_connection_refused_error_without_server(self): 

Do you already know this test when you run it? Do you really need to use underscores? are words like "that" really necessary to understand the name? would a camel case be equally readable? how about the first example below as rewriting above (number of characters = 79): Accepting an agreement to use abbreviations for a small collection of common words is even more efficient, for example. Connection = Conn, Error = Err. When using abbreviations, you should remember the context and use them only when there is no possibility of embarrassment - the second example below. If you agree that there is no actual need to mention the client as a test object in the method name, since this information is in the class name, then a third example may be appropriate. (54).

ClientEventListenerReceivesConnectionRefusedErrorWithoutServer (itself):

ClientEventListenerReceivesConnRefusedErrWithoutServer (itself):

EventListenerReceiveConnRefusedErrWithoutServer (itself):

I also agree with the suggestion from B Rad C "to use a descriptive name as msg kwarg arg in in self.assert". You should only be interested in seeing the result of failed tests when starting testuite. Verifying that you have all the necessary tests should not depend on the method names being so verbose.

PS Perhaps I will also remove "Serverless" as an extra. Should a client event handler receive an event if the server is not connected for any reason? (although tbh I would have thought that it would be better if the client could not connect to the server, it received some kind of “connection is unavailable”, the denial of connection suggests that the server can be found, but it refuses the connection itself.)

+5
Dec 04 '16 at 13:22
source share

The need for such names may hint at other smells.

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

ClientConnectionTest sounds pretty broad (and not at all like the device under test), and it's probably a large class with a lot of tests inside that can be reoriented. Like this:

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

A "test" is not useful for a name because it is implied.

With all the code you gave me, my last piece of advice is to reorganize your test code and then reconsider your problem (if it is still there).

+5
Dec 08 '16 at 21:10
source share



All Articles