86 votes

Est-il possible de séparer un nom de fonction long sur plusieurs lignes?

Notre équipe de développement utilise un PEP8 linter qui nécessite un maximum de longueur de ligne de 80 caractères.

Quand je suis en train d'écrire des tests unitaires en python, j'aime avoir descriptives des noms de méthode pour décrire ce que chaque test. Toutefois cela conduit souvent à me dépasser la limite de caractères.

Voici un exemple d'une fonction qui est trop 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()

Mes Options:

  • Vous pouvez simplement écrire moins les noms de méthode!

    Je sais, mais je ne veux pas perdre le caractère descriptif des noms de tests.

  • Vous pouvez écrire les commentaires multi-lignes au-dessus de chaque test au lieu d'utiliser des noms longs!

    C'est un bon idée, mais alors je ne vais pas être en mesure de voir le test de noms lors de l'exécution de tests à l'intérieur de mon IDE (PyCharm).

  • Peut-être vous pouvez continuer les lignes avec une barre oblique inverse (une logique caractère de continuation de ligne).

    Malheureusement, ce n'est pas une option en Python, comme mentionné dans Dan réponse.

  • Vous pourriez arrêter peluchage vos tests.

    Cela a un sens, à certains égards, mais c'est sympa d'encourager un bien formaté suite de tests.

  • Vous pouvez augmenter la longueur de la ligne de limite.

    Notre équipe aime avoir de la limite, car il permet de conserver un code lisible sur d'étroites affiche, donc ce n'est pas la meilleure option.

  • Vous pouvez supprimer test depuis le début de vos méthodes.

    Ce n'est pas une option. Python test coureur a besoin de toutes les méthodes d'essai pour commencer avec test ou de ne pas les ramasser.

    Edit: Certains les coureurs de l'épreuve vous permettent de spécifier une expression régulière lors de la recherche pour les fonctions de test, bien que je préfère ne pas le faire parce que c'est extra d'installation pour tous ceux qui travaillent sur le projet. Aussi, il n'a pas vraiment répondre à la question d'origine.

  • Vous pourriez séparer l'EventListener dans sa propre classe et de le tester séparément.

    L'Écouteur d'Événement est dans sa propre classe (et testée). C'est juste une interface qui est déclenchée par des événements qui se passe à l'intérieur de ClientConnection. Ce genre de suggestion semble avoir de bonnes intentions, mais d'une erreur d'acheminement et de ne pas permettre de répondre à la question d'origine.

  • Vous pouvez utiliser une BDD Cadre comme se Comporter. Il est conçu pour expressifs tests.

    C'est vrai, et j'espère que d'utiliser plusieurs d'entre eux dans l'avenir. Bien que j'ai tout de même envie de savoir comment diviser les noms de fonction à travers les lignes.

En fin de compte...

Est-il possible en Python pour diviser une longue déclaration de fonction sur plusieurs lignes?

Par exemple...

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

Ou vais-je mordre la balle et de raccourcir moi-même?

80voto

Dan Points 18831

Non, ce n'est pas possible.

Dans la plupart des cas tels un nom long serait pas souhaitable du point de vue de la lisibilité et la facilité d'utilisation de la fonction, si votre cas d'utilisation de noms de tests semble assez raisonnable.

Les règles lexicales de Python ne permettent pas à un seul jeton (dans ce cas, un identifiant) à répartir sur plusieurs lignes. La logique caractère de continuation de ligne (\ à la fin d'une ligne) peut joindre plusieurs lignes physiques dans une logique unique de la ligne, mais ne peut pas joindre un seul jeton sur plusieurs lignes.

53voto

Sean Vieira Points 47080

Vous pouvez également écrire un décorateur qui mute .__name__ pour la méthode.

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

Ensuite, vous pourriez écrire:

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

en s'appuyant sur le fait que Python concatène les littéraux de chaîne adjacents à la source.

36voto

mattmc3 Points 6768

Selon la réponse à cette question: Comment désactiver une erreur pep8 dans un fichier spécifique? , utilisez le commentaire de fin # nopep8 ou # noqa pour désactiver PEP-8 pour une longue ligne. Il est important de savoir quand enfreindre les règles. Bien sûr, le Zen of Python vous dirait que "les cas particuliers ne sont pas assez spéciaux pour enfreindre les règles".

9voto

Skycc Points 2539

Nous pouvons appliquer le décorateur de la classe au lieu de la méthode depuis unittest méthodes get nom d' dir(class).

Le décorateur decorate_method vont à travers les méthodes de la classe et de renommer la méthode nom basé sur func_mapping dictionnaire.

La pensée de cela, après avoir vu le décorateur réponse de @Sean Vieira , +1 de moi

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

essai avec l' unittest comme ci-dessous fait apparaître le long descriptive nom de la fonction, pense qu'il pourrait fonctionne pour votre cas, si elle ne peut pas les sons de manière élégante et lisible de la mise en œuvre

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

7voto

alecxe Points 50783

Une sorte de contexte spécifiques à l'approche du problème. Le cas de test que vous avez présenté en fait ressemble beaucoup à un Langage Naturel format de décrire les étapes nécessaires pour un cas de test à prendre.

Voir si l'aide de l' behave le Comportement du Pilote de développement cadre de style aurait plus de sens ici. Votre "fonction" (voir comment l' given, when, then de refléter ce que vous avez eu):

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

Il est également pertinente pyspecs package, exemple d'utilisation d'une récente réponse sur un sujet connexe:

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X