ruby - run - ¿Es posible que RSpec espere cambios en dos tablas?
shoulda-matchers (8)
Después de que ninguna de las soluciones propuestas demostró realmente funcionar, logré esto agregando un change_multiple
matcher. Esto solo funcionará para RSpec 3 y no para 2. *
module RSpec
module Matchers
def change_multiple(receiver=nil, message=nil, &block)
BuiltIn::ChangeMultiple.new(receiver, message, &block)
end
alias_matcher :a_block_changing_multiple, :change_multiple
alias_matcher :changing_multiple, :change_multiple
module BuiltIn
class ChangeMultiple < Change
private
def initialize(receiver=nil, message=nil, &block)
@change_details = ChangeMultipleDetails.new(receiver, message, &block)
end
end
class ChangeMultipleDetails < ChangeDetails
def actual_delta
@actual_after = [@actual_after].flatten
@actual_before = [@actual_before].flatten
@actual_after.map.with_index{|v, i| v - @actual_before[i]}
end
end
end
end
end
ejemplo de uso:
it "expects multiple changes despite hordes of cargo cultists chanting aphorisms" do
a = "." * 4
b = "." * 10
times_called = 0
expect {
times_called += 1
a += ".."
b += "-----"
}.to change_multiple{[a.length, b.length]}.by([2,5])
expect(times_called).to eq(1)
end
Hacer que by_at_least
y by_at_most
funcionen para change_multiple requeriría un trabajo adicional.
RSpec espera un cambio:
it "should increment the count" do
expect{Foo.bar}.to change{Counter.count}.by 1
end
¿Hay alguna manera de esperar cambios en dos tablas?
expect{Foo.bar}.to change{Counter.count}.by 1
and change{AnotherCounter.count}.by 1
Esto debería ser dos pruebas. Las mejores prácticas de RSpec requieren una afirmación por prueba .
describe "#bar" do
subject { lambda { Foo.bar } }
it { should change { Counter.count }.by 1 }
it { should change { AnotherCounter.count }.by 1 }
end
Estoy ignorando las mejores prácticas por dos razones:
- Un conjunto de mis pruebas son pruebas de regresión, quiero que corran rápido y rara vez se rompen. La ventaja de tener claridad acerca de qué se está rompiendo exactamente no es enorme, y la desaceleración de la refacturación de mi código para que se ejecute el mismo evento varias veces es importante para mí.
- A veces soy un poco flojo, y es más fácil no hacer ese refactor
La forma en que hago esto (cuando tengo que hacerlo) es confiar en el hecho de que mi base de datos comienza vacía, entonces podría escribir:
foo.bar
expect(Counter.count).to eq(1)
expect(Anothercounter.count).to eq(1)
En algunos casos, mi base de datos no está vacía, pero conozco el recuento anterior o puedo probar explícitamente el recuento anterior:
counter_before = Counter.count
another_counter_before = Anothercounter.count
foo.bar
expect(Counter.count - counter_before).to eq(1)
expect(Anothercounter.count - another_counter_before).to eq(1)
Finalmente, si tiene que controlar muchos objetos (a veces lo hago), puede hacerlo como:
before_counts = {}
[Counter, Anothercounter].each do |classname|
before_counts[classname.name] = classname.count
end
foo.bar
[Counter, Anothercounter].each do |classname|
expect(classname.count - before_counts[classname.name]).to be > 0
end
Si tiene necesidades similares a mí, esto funcionará, mi único consejo sería hacer esto con los ojos abiertos: las otras soluciones propuestas son más elegantes, pero solo tienen un par de inconvenientes en ciertas circunstancias.
La mejor manera que he encontrado es hacerlo "manualmente":
counters_before = Counter.count
another_counters_before = AnotherCounter.count
Foo.bar
expect(Counter.count).to eq (counters_before + 1)
expect(AnotherCounter.count).to eq (another_counters_before + 1)
No es la solución más elegante, pero funciona
La sintaxis de Georg Ladermann es más agradable, pero no funciona. La forma de probar los cambios de valores múltiples es combinar los valores en matrices. De lo contrario, solo la última afirmación de cambio decidirá sobre la prueba.
Así es como lo hago:
it "should increment the counters" do
expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1])
end
Esto funciona perfectamente con la función ''.to''.
Prefiero esta sintaxis (rspec 3 o posterior):
it "should increment the counters" do
expect { Foo.bar }.to change { Counter, :count }.by(1).and /
change { AnotherCounter, :count }.by(1)
end
Sí, estas son dos afirmaciones en un solo lugar, pero dado que el bloque se ejecuta solo una vez, puede acelerar las pruebas.
EDITAR: Se agregó una barra invertida después de .and
para evitar el error de sintaxis
Recibí errores de sintaxis intentando usar la solución de @JohnJohnston ; esta es la forma que finalmente funcionó para mí:
it "should increment the counters" do
expect { Foo.bar }.to change { Counter.count }.by(1)
.and change { AnotherCounter.count }.by(1)
end
Debo mencionar que estoy usando ruby 2.2.2p95. No sé si esta versión tiene algún cambio sutil en el análisis que me causa errores, pero no parece que nadie más en este hilo haya tenido ese problema.
Si no desea utilizar el enfoque basado en la taquigrafía / contexto sugerido anteriormente, también puede hacer algo como esto, pero tenga en cuenta que ejecutará la expectativa dos veces, por lo que podría no ser apropiado para todas las pruebas.
it "should increment the count" do
expectation = expect { Foo.bar }
expectation.to change { Counter.count }.by 1
expectation.to change { AnotherCounter.count }.by 1
end