Is it possible to break a long function name across multiple lines?

后端 未结 7 1122
南旧
南旧 2021-02-04 23:02

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

When I\'m writing unit tests in python, I like to have

相关标签:
7条回答
  • 2021-02-04 23:32

    Per the answer to this question:How to disable a pep8 error in a specific file?, use the # nopep8 or # noqa trailing comment to disable PEP-8 for a long line. It's important to know when to break the rules. Of course, the Zen of Python would tell you that "Special cases aren't special enough to break the rules."

    0 讨论(0)
  • 2021-02-04 23:32

    The need for this kind of 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 a testable unit), and is likely a large class with plenty of tests inside that could be refocused. Like this:

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

    "Test" is not useful in the name because it's implied.

    With all the code you've given me, my final advice is: refactor your test code, then revisit your problem (if it's still there).

    0 讨论(0)
  • 2021-02-04 23:33

    No, this is not possible.

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

    The lexical rules of Python do not allow a single token (in this case an identifier) to be split across multiple lines. The logical line continuation character (\ at the end of a line) can join multiple physical lines into a single logical line, but cannot join a single token across multiple lines.

    0 讨论(0)
  • 2021-02-04 23:33

    You could 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 the fact that Python concatenates source-adjacent string literals.

    0 讨论(0)
  • 2021-02-04 23:37

    The shorter function name solution has a lot of merit. Think about what is really needed in your actual function name and what is supplied already.

    test_that_client_event_listener_receives_connection_refused_error_without_server(self):
    

    Surely you already know it's a test when you run it? Do you really need to use underscores? are words like 'that' really required for the name to be understood? would camel case be just as readable? how about the first example below as a rewriting of the above (character count = 79): Accepting a convention to use abbreviations for a small collection of common words is even more effective, e.g. Connection = Conn, Error = Err. When using abbreviations you have to be mindful of the context and only use them when there is no possiblity of confusion - Second example below. If you accept that there's no actual need to mention the client as the test subject in the method name as that information is in the class name then the third example may be appropriate. (54) characters.

    ClientEventListenerReceivesConnectionRefusedErrorWithoutServer(self):

    ClientEventListenerReceivesConnRefusedErrWithoutServer(self):

    EventListenerReceiveConnRefusedErrWithoutServer(self):

    I'd also agree with the the suggestion from B Rad C "use descriptive name as the msg kwarg arg in in a self.assert" You should only be interested in seeing output from failed tests when the testsuite is run. Verification that you have all the necessary tests written shouldn't depend on having the method names so detailed.

    P.S. I'd probably also remove 'WithoutServer' as superfluous as well. Shouldn't the client event handler receive the event in the case that the server isn't contactable for any reason? (although tbh I'd think that it would be better that if they client can't connect to a server it receives some sort of 'connection unavailable' , connection refused suggests that the server can be found but refuses the connection itself.)

    0 讨论(0)
  • 2021-02-04 23:44

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

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

    Thought of this after seeing 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 below did show the full long descriptive function name, thinks it might works for your case though it may not sounds 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
    
    0 讨论(0)
提交回复
热议问题