Factory_bot: appliquer des traits aléatoires dans Factory

Créé le 26 sept. 2014  ·  4Commentaires  ·  Source: thoughtbot/factory_bot

Salut,
Je travaille actuellement sur certaines usines et je souhaite avoir chacune des valeurs statiques et des valeurs aléatoires. Pour utiliser les traits déjà définis, je voudrais quelque chose comme :

FactoryGirl.define do

  factory :currency do
    eur

    trait :usd do
      name 'US-Dollar'
      symbol '$'
    end

    trait :eur do
      name 'Euro'
      symbol '€'
    end

    # what would be really nice..
    trait :random, traits: [
        [:eur, :usd].sample
    ]

  end
end

Pourrions-nous ajouter cette fonctionnalité dans une future version, ou est-il déjà possible de le faire ?

Commentaire le plus utile

@joshuaclayton Je n'utilise pas d'outil de test de fuzz en tant que tel, mais mes usines et mes tests unitaires ont toujours autant que possible des données aléatoires. Et je ne suis pas d'accord avec la citation de Fowler que vous avez publiée : alors que les _données_ de mes tests peuvent être non déterministes, la relation entre l'entrée et la sortie est complètement déterministe, donc je n'ai jamais eu de problème à dire si j'ai un bogue ou un cas particulier. Peu importe de toute façon : si je ne gère pas correctement un cas limite, cela _est_ un bogue, que ce soit dans mon test ou dans mon implémentation. Je n'ignore certainement pas les tests qui échouent.

Ne pas utiliser de données aléatoires dans vos tests introduit un biais de confirmation : vous serez tenté de ne tester que les cas dont vous vous attendez déjà à réussir ou à échouer, votre test ne sera donc pas vraiment utile. Les données aléatoires, avec une relation déterministe bien définie entre l'entrée et la sortie, vous permettent de rester honnête.


Exemple de la façon dont j'écris les tests :

# models/user.rb
class User < ActiveRecord::Base
  def uppercase_name
    name.upcase
  end
end
# factories/user.rb
FactoryGirl.define do
  factory :user
    name { Faker::Name.name }
  end
end
# spec/models/user_spec.rb
describe User, '#uppercase_name' do
  it "returns the user's name in uppercase"
    user = FactoryGirl.create(:user)
    expect(user.uppercase_name).to be == user.name.upcase
  end
end

La relation entre l'entrée et la sortie est donc complètement déterministe : "John" => "JOHN", "Billy Bob" => "BILLY BOB", et ainsi de suite. Mais le code RSpec est plus révélateur d'intention (car il doit décrire la relation, pas un résultat codé en dur), et je ne peux pas casser l'application en écrivant une implémentation qui ne passera pas tous les cas.

Tous les 4 commentaires

@loybert Vous pouvez techniquement le faire lors de la construction d'usines : create(:currency, [:eur, :usd].sample)

Cela dit, je vous encourage fortement à _ne pas_ utiliser des données aléatoires dans vos suites de tests d'acceptation ou de tests unitaires - j'ai découvert à maintes reprises que l'introduction de valeurs aléatoires dans les suites de tests ne finissait que par me cogner la tête contre le mur.

?? J'utilise régulièrement des données aléatoires dans mes suites de tests. Je n'ai jamais trouvé que cela causait de problèmes du tout, et parfois cela signale des cas que je n'ai pas envisagés. Je pense que les données fixes sont une odeur de test.

@marnen je pense que cela dépend des tests; Je peux certainement apprécier un outil de fuzzing, comme tarentula , pour essayer activement de casser des zones d'une application.

Cependant, j'ai trouvé des données aléatoires dans des tests qui ne sont pas explicitement écrits pour fuzz l'application introduit souvent le non-déterminisme, dont Martin Fowler a longuement parlé ici .

Le problème avec les tests non déterministes est que lorsqu'ils deviennent rouges, vous ne savez pas si cela est dû à un bogue ou simplement à une partie du comportement non déterministe. Habituellement, avec ces tests, un échec non déterministe est relativement courant, vous finissez donc par hausser les épaules lorsque ces tests deviennent rouges. Une fois que vous commencez à ignorer l'échec d'un test de régression, alors ce test est inutile et vous pourriez aussi bien le jeter.

Si je teste une interface d'un objet où les résultats sont les mêmes, je m'appuierais sur Ruby et mon framework de test pour générer des tests déterministes pour plusieurs cas, donc si le test échoue, je sais exactement où, comment et le l'état dans lequel se trouve l'objet lorsqu'il échoue. Toute information inférieure, au point de Martin Fowler, est inutile car elle ne donne aucun retour exploitable autre que le fait que _quelque chose_ est cassé.

@joshuaclayton Je n'utilise pas d'outil de test de fuzz en tant que tel, mais mes usines et mes tests unitaires ont toujours autant que possible des données aléatoires. Et je ne suis pas d'accord avec la citation de Fowler que vous avez publiée : alors que les _données_ de mes tests peuvent être non déterministes, la relation entre l'entrée et la sortie est complètement déterministe, donc je n'ai jamais eu de problème à dire si j'ai un bogue ou un cas particulier. Peu importe de toute façon : si je ne gère pas correctement un cas limite, cela _est_ un bogue, que ce soit dans mon test ou dans mon implémentation. Je n'ignore certainement pas les tests qui échouent.

Ne pas utiliser de données aléatoires dans vos tests introduit un biais de confirmation : vous serez tenté de ne tester que les cas dont vous vous attendez déjà à réussir ou à échouer, votre test ne sera donc pas vraiment utile. Les données aléatoires, avec une relation déterministe bien définie entre l'entrée et la sortie, vous permettent de rester honnête.


Exemple de la façon dont j'écris les tests :

# models/user.rb
class User < ActiveRecord::Base
  def uppercase_name
    name.upcase
  end
end
# factories/user.rb
FactoryGirl.define do
  factory :user
    name { Faker::Name.name }
  end
end
# spec/models/user_spec.rb
describe User, '#uppercase_name' do
  it "returns the user's name in uppercase"
    user = FactoryGirl.create(:user)
    expect(user.uppercase_name).to be == user.name.upcase
  end
end

La relation entre l'entrée et la sortie est donc complètement déterministe : "John" => "JOHN", "Billy Bob" => "BILLY BOB", et ainsi de suite. Mais le code RSpec est plus révélateur d'intention (car il doit décrire la relation, pas un résultat codé en dur), et je ne peux pas casser l'application en écrivant une implémentation qui ne passera pas tous les cas.

Cette page vous a été utile?
0 / 5 - 0 notes