Можно ли разбить длинное название на несколько строк?



наша команда разработчиков использует Линтер PEP8, который требует максимальная длина строки 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, как упоминалось в ответе Дэна.





  • вы могли бы остановить пылеобразования тестов.




    это имеет смысл в некотором смысле, но приятно поощрять хорошо структурированных тестов.





  • вы можете увеличить предел длины линии.




    наша команда любит ограничение, потому что это помогает держать код читаемым на узких дисплеях, так что это не лучшие.





  • убрать test С самого начала ваших методов.




    это не вариант. Тестовый бегун Python нуждается во всех методах тестирования, чтобы начать с test или он не заберет их.



    Edit: некоторые тестовые бегуны позволяют указать регулярное выражение при поиске тестовых функций, хотя я бы а не делать этого, потому что это дополнительная настройка для всех, кто работает над проектом. Кроме того, это не совсем ответ на изначальный вопрос.





  • вы можете разделить EventListener на свой собственный класс и протестировать его отдельно.




    Прослушиватель Событий и в своем классе (и тестируется). Это просто интерфейс, который запускается событиями, происходящими в ClientConnection. Такое предложение кажется чтобы иметь хорошие намерения, но неверно направлен и не помогает ответить на исходный вопрос.





  • вы могли бы использовать структуру 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()


или мне придется укусить пулю и укоротить ее самостоятельно?

327   7  

7 ответов:

нет, это невозможно.

в большинстве случаев такое длинное имя было бы нежелательным с точки зрения читаемости и удобства использования функции, хотя ваш вариант использования для тестовых имен кажется довольно разумным.

на лексические правила Python Не допускайте разбиения одного токена (в данном случае идентификатора) на несколько строк. Символ продолжения логической строки (\ в конце строки) можно объединить несколько физических строк в одна логическая строка, но не может присоединиться к одной маркер на несколько строк.

вы может также написать декоратор, который мутирует .__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 конкатенирует исходные соседние строковые литералы.

в ответ на этот вопрос:Как отключить ошибку pep8 в конкретном файле? используйте # nopep8 или # noqa трейлинг комментарий, чтобы отключить PEP-8 для длинной линии. Важно знать, когда нарушать правила. Конечно, Дзен питона сказал бы вам, что " особые случаи не являются достаточно особенными, чтобы нарушить правила."

мы можем применять оформителя к классу вместо метода, так как unittest получить имя метода от 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()

тестовый запуск с unittest как показано ниже, полное длинное описательное имя функции, думает, что это может работать для вашего случая хотя это может звучать не так элегантно и читаемый из реализации

>>> unittest.main(verbosity=2)
test_that_client_event_listener_receives_connection_refused_error_without_server (__main__.ClientConnectionTest) ... i am client_test
ok

своего рода контекстно-специфический подход к проблеме. Тестовый случай, который вы представили, на самом деле очень похож на формат естественного языка описания необходимых шагов для выполнения тестового случая.

смотрите, если с помощью 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

там тоже уместно pyspecs пакета, пример использования из недавнего ответа на связанную тему:

более короткое решение имени функции имеет много достоинств. Подумайте о том, что такое действительно требуется в вашем фактическом имени функции и то, что уже поставляется.

test_that_client_event_listener_receives_connection_refused_error_without_server(self):

конечно, вы уже знаете, что это испытание, когда вы запустите его? Вам действительно нужно использовать подчеркивания? действительно ли такие слова, как "это", необходимы для понимания имени? будет ли случай с верблюдом таким же читаемым? как насчет первого примера ниже, как переписывание выше (количество символов = 79): Принятие соглашения об использовании сокращений для небольшого набора общих слов еще более эффективно, например Connection = Conn, Error = Err. При использовании сокращений вы должны помнить о контексте и использовать их только тогда, когда нет возможности путаницы - второй пример ниже. Если вы согласны с тем, что нет фактической необходимости упоминать клиента в качестве испытуемого в имени метода, поскольку эта информация находится в имени класса, то третий пример может быть подходящим. (54) письмена.

ClientEventListenerReceivesConnectionrefusederrorwithoutserver (self):

ClientEventListenerReceivesConnRefusederrwithoutserver (self):

EventListenerReceiveConnRefusedErrwithoutserver (self):

Я бы также согласился с предложением от B Rad C " использовать описательное имя в качестве msg kwarg arg в себе.assert " вы должны быть заинтересованы только в том, чтобы увидеть вывод из неудачных тестов при запуске testsuite. Проверка того, что вы наличие всех необходимых тестов не должно зависеть от того, что имена методов настолько детализированы.

P. S. Я бы, наверное, также удалить 'WithoutServer' за ненадобностью, а также. Разве обработчик событий клиента не должен получать событие в случае, если сервер не может связаться по какой-либо причине? (хотя tbh я думаю , что было бы лучше, если бы клиент не мог подключиться к серверу, он получает какое-то "соединение недоступно", отказ от подключения предполагает, что сервер может быть найдено, но отказывается от самого соединения.)

потребность в такого рода именах может намекать на другие запахи.

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):
      ...

"тест" не полезен в названии, потому что это подразумевается.

со всем кодом, который вы мне дали, мой последний совет: рефакторинг вашего тестового кода, а затем вернуться к вашей проблеме (если она все еще там).

Comments

    Ничего не найдено.