J'utilise Ruby on Rails 4 et le gem 2.14 rspec-rails. Pour mon objet, je voudrais comparer l'heure actuelle avec l' updated_at
attribut d'objet après l'exécution d'une action du contrôleur, mais je suis en difficulté car la spécification ne passe pas. Autrement dit, étant donné ce qui suit est le code de spécification:
it "updates updated_at attribute" do
Timecop.freeze
patch :update
@article.reload
expect(@article.updated_at).to eq(Time.now)
end
Lorsque j'exécute les spécifications ci-dessus, j'obtiens l'erreur suivante:
Failure/Error: expect(@article.updated_at).to eq(Time.now)
expected: 2013-12-05 14:42:20 UTC
got: Thu, 05 Dec 2013 08:42:20 CST -06:00
(compared using ==)
Comment puis-je faire passer les spécifications?
Remarque : j'ai également essayé ce qui suit (notez l' utc
ajout):
it "updates updated_at attribute" do
Timecop.freeze
patch :update
@article.reload
expect(@article.updated_at.utc).to eq(Time.now)
end
mais la spécification ne passe toujours pas (notez la différence de valeur "obtenue"):
Failure/Error: expect(@article.updated_at.utc).to eq(Time.now)
expected: 2013-12-05 14:42:20 UTC
got: 2013-12-05 14:42:20 UTC
(compared using ==)
===
, mais cela peut souffrir du franchissement des secondes limites. Le mieux est probablement de trouver ou d'écrire votre propre matcher, dans lequel vous convertissez en secondes d'époque et permettez une petite différence absolue.===
au lieu de==
- vous comparez actuellement object_id de deux objets Time différents. Bien que Timecop ne gèle pas l'heure du serveur de base de données. . . donc si vos horodatages sont générés par le SGBDR, cela ne fonctionnera pas (je suppose que ce n'est pas un problème pour vous ici cependant)Réponses:
L'objet Ruby Time maintient une plus grande précision que la base de données. Lorsque la valeur est lue à partir de la base de données, elle n'est conservée qu'à la microseconde, tandis que la représentation en mémoire est précise en nanosecondes.
Si vous ne vous souciez pas de la différence en millisecondes, vous pouvez faire un to_s / to_i des deux côtés de votre attente
ou
Reportez-vous à ceci pour plus d'informations sur les raisons pour lesquelles les heures sont différentes
la source
Timecop
gem. Doit-il simplement résoudre le problème en "figeant" l'heure?be_within
la bonneexpect {FooJob.perform_now}.to have_enqueued_job(FooJob).at(some_time)
Je ne suis pas sûr que le.at
matcher accepterait un temps converti en entier avec.to_i
quelqu'un a rencontré ce problème?Je trouve que l'utilisation du
be_within
matcher rspec par défaut est plus élégante:la source
to_s
. Aussi,to_i
etto_s
peut échouer rarement si le temps est proche de la fin d'une seconde.be_within
matcher a été ajouté à RSpec 2.1.0 le 7 novembre 2010, et amélioré plusieurs fois depuis. rspec.info/documentation/2.14/rspec-expectations/…undefined method 'second' for 1:Fixnum
. Y a-t-il quelque chose dont j'ai besoinrequire
?.second
est une extension de rails: api.rubyonrails.org/classes/Numeric.htmloui, car cela
Oin
suggère que lebe_within
matcher est la meilleure pratique... et il a quelques autres cas d'utilisation -> http://www.eq8.eu/blogs/27-rspec-be_within-matcher
Mais une autre façon de gérer cela est d'utiliser les Rails intégrés
midday
et lesmiddnight
attributs.Maintenant, c'est juste pour la démonstration!
Je n'utiliserais pas cela dans un contrôleur car vous stubbing tous les appels Time.new => tous les attributs de temps auront le même temps => peuvent ne pas prouver le concept que vous essayez d'atteindre. Je l'utilise généralement dans des objets Ruby composés similaires à ceci:
Mais honnêtement, je recommande de rester avec
be_within
matcher même lorsque vous comparez Time.now.midday!Alors oui, veuillez coller avec le
be_within
matcher;)mise à jour 2017-02
Question en commentaire:
vous pouvez passer n'importe quel matcher RSpec au
match
matcher (vous pouvez donc même faire des tests API avec RSpec pur )En ce qui concerne "post-db-times", je suppose que vous voulez dire une chaîne qui est générée après l'enregistrement dans DB. Je suggérerais de découpler ce cas en 2 attentes (une assurant la structure de hachage, une deuxième vérification de l'heure) Vous pouvez donc faire quelque chose comme:
Mais si ce cas est trop souvent dans votre suite de tests, je suggérerais d'écrire votre propre matcher RSpec (par exemple
be_near_time_now_db_string
) en convertissant le temps de la chaîne db en objet Time, puis de l'utiliser dans le cadre dematch(hash)
:la source
expect(hash_1).to eq(hash_2)
fonctionner quand certaines valeurs de hash_1 sont pré-db-times et les valeurs correspondantes dans hash_2 sont post-db-times?Ancien message, mais j'espère que cela aidera quiconque entre ici pour une solution. Je pense qu'il est plus facile et plus fiable de simplement créer la date manuellement:
Cela garantit que la date stockée est la bonne, sans faire
to_x
ni se soucier des décimales.la source
Vous pouvez convertir l'objet date / datetime / time en une chaîne tel qu'il est stocké dans la base de données avec
to_s(:db)
.la source
Le moyen le plus simple que j'ai trouvé autour de ce problème est de créer une
current_time
méthode d'assistance de test comme ceci:Désormais, l'heure est toujours arrondie à la milliseconde la plus proche pour que les comparaisons soient simples:
la source
.change(usec: 0)
est très utile.change(usec: 0)
astuce pour résoudre le problème sans utiliser également un assistant de spécification. Si la première ligne est,Timecop.freeze(Time.current.change(usec: 0))
nous pouvons simplement comparer.to eq(Time.now)
à la fin.Parce que je comparais les hachages, la plupart de ces solutions ne fonctionnaient pas pour moi, alors j'ai trouvé que la solution la plus simple était simplement de récupérer les données du hachage que je comparais. Étant donné que les heures de mise à jour ne sont pas vraiment utiles pour moi, cela fonctionne correctement.
la source