Un test unitaire typique de RSpec utilise intensivement des blocs imbriqués Ruby) afin de structurer le code et d'utiliser la magie "DSL" pour que les spécifications se lisent comme des instructions BDD:
describe Foo do
context "with a bar" do
before :each do
subject { Foo.new().add_bar }
end
it "looks like a baz" do
expect # etc
Dans une spécification idéale, chaque exemple peut être relativement court et précis. Cependant, il semble habituel que les blocs externes atteignent plus de 100 lignes, car la structure RSpec fonctionne de cette manière et ne nécessite pas beaucoup d'exemples spécifiques, chacun d'entre eux pouvant comporter quelques lignes d'installation spécifique, pour arriver à describe
blocs de même taille ou plus grands que le code du sujet décrit.
Une mise à jour récente de Rubocop a mis en place une nouvelle règle selon laquelle les blocs ne doivent pas dépasser 25 lignes. Je ne suis pas sûr de la raison, parce que cela ne figure pas dans le guide de style Ruby . Je peux voir pourquoi cela pourrait être une bonne chose, et ajouté au jeu de règles par défaut. Cependant, après la mise à niveau, notre test Rubocop échoue plusieurs fois avec des messages tels que tests/component_spec.rb:151:3: C: Block has too many lines. [68/25]
Avec des outils de métrique de code tels que Rubocop, je aime avoir une politique de "Utiliser les valeurs par défaut, lien vers le guide de style, travail effectué". (principalement parce que débattre des tabulations vs des espaces et d’autres minuties fait perdre du temps, et que IME n’est jamais résolu ) Ici, ce n’est clairement pas possible, deux de nos données de base les outils qualité ne sont pas d’accord sur l’approche de présentation du code - ou du moins c’est ainsi que j’interprète les résultats, je ne vois rien de fondamentalement faux dans la façon dont nous avons écrit les spécifications.
En réponse, nous avons simplement défini la règle de taille de bloc Rubocop sur un seuil élevé. Mais cela me fait me demander: qu'est-ce qui me manque? RSpec utilise-t-il une approche maintenant discréditée pour la structure de code et quelles sont les options raisonnables permettant de réduire la taille des blocs dans nos tests RSpec? Je peux voir des moyens de restructurer le code pour éviter les gros blocs, mais ils sont sans exception des hacks moches destinés uniquement à respecter la règle de Rubocop, par exemple. décomposez tous les blocs en fonctions d'assistance:
def looks_like_a_baz
it "looks like a baz" do
expect # etc
end
end
def bar_context
context "with a bar" do
before :each do
subject { Foo.new().add_bar }
end
looks_like_a_baz
end
end
describe Foo do
bar_context
# etc
. . . Je veux dire, c'est faisable, mais transformer de cette manière une multitude d'exemples de spécifications en fonctions d'assistance semble être l'opposé de l'approche lisible encouragée par la conception RSpec.
Y a-t-il autre chose que je puisse faire, à part trouver des moyens de l'ignorer?
La question la plus proche que j'ai pu trouver sur ce sujet ici était RSpec & Rubocop/Ruby Guide de styles et cela semblait résolu en modifiant des modèles de test.
Une mise à jour récente de Rubocop a mis en place une nouvelle règle selon laquelle les blocs ne doivent pas dépasser 25 lignes. Je ne suis pas sûr de la raison, parce que cela n’apparaît pas dans le guide de style Ruby).
Auparavant, tous les flics étaient basés sur le Guide de style Ruby), et RuboCop était un moyen de respecter les pratiques définies par la communauté.
Depuis, l'orientation a changé et le champ d'application de RuboCop s'est élargi pour aider les développeurs à assurer la cohérence de leurs bases de code en général. Cela a conduit à deux choses:
Ce flic tombe dans la deuxième catégorie.
RSpec utilise-t-il une approche maintenant discréditée pour la structure de code et quelles sont les options raisonnables pour réduire la taille des blocs dans nos tests RSpec?
La réponse courte est non. Les DSL sont toujours cool. :-)
Ce flic est destiné à bloc large dans le sens de la programmation impérative. En règle générale, cela ne s'applique pas aux DSL, qui sont souvent déclaratifs. Par exemple, avoir un long fichier routes.rb
Dans Rails est parfaitement bénin. Il s'agit simplement du résultat naturel d'une application volumineuse plutôt que d'une violation de style. ( tests est purement génial.)
Maintenant, RuboCop est assez intelligent, mais il ne sait pas ce qu'est un ADSL et ne le sait pas. Nous ne pouvons donc pas les ignorer automatiquement. On pourrait penser que nous pourrions exclure les méthodes d’entrée DSL des frameworks populaires, tels que Rails routes et spécifications RSpec. Les raisons de ne pas le faire sont principalement:
/spec
Est une courtoisie jusqu'à ce que nous ayons un système d'extension approprié. être manipulé par la gemme rubocop-rspec
.)Je veux dire, c'est faisable, mais transformer de cette manière une multitude d'exemples de spécifications en fonctions d'assistance semble être l'opposé de l'approche lisible encouragée par la conception RSpec.
Le résultat est le suivant: RuboCop est là pour nous aider à écrire un meilleur code. Si la conception de nos applications est par ailleurs solide et que nous rendons les choses moins lisibles simplement pour faire plaisir à RuboCop, nous devrions alors filtrer, configurer ou désactiver le flic. :-)
En réponse, nous avons simplement défini la règle de taille de bloc Rubocop sur un seuil élevé. Mais cela me fait me demander: qu'est-ce qui me manque?
Il s’agit d’un outil assez grossier et, comme vous le dites, vous aurez probablement de faux négatifs à cause de cela. Il existe deux types de faux positifs pour ce flic:
aasm
dans un modèle Rails.Dans le premier cas, la meilleure solution consiste à exclure le fichier ou le répertoire et dans le second à utiliser une désactivation en ligne.
Dans votre cas, vous devriez mettre à jour votre .rubocop.yml
Avec:
Metrics/BlockLength:
Exclude:
- 'Rakefile'
- '**/*.rake'
- 'test/**/*.rb'
(Notez que vous devez réitérer les exclus de base de la configuration par défaut, car la liste sera remplacée.)
Si un bloc spécifique est généralement trop long, je le spécifie plutôt que les fichiers
Metrics/BlockLength:
ExcludedMethods: ['describe', 'context']