Rspec: "array.should == another_array" pero sin preocuparse por el orden

235

A menudo quiero comparar matrices y asegurarme de que contengan los mismos elementos, en cualquier orden. ¿Hay una manera concisa de hacer esto en RSpec?

Aquí hay métodos que no son aceptables:

#to_set

Por ejemplo:

expect(array.to_set).to eq another_array.to_set

o

array.to_set.should == another_array.to_set

Esto falla cuando las matrices contienen elementos duplicados.

#sort

Por ejemplo:

expect(array.sort).to eq another_array.sort

o

array.sort.should == another_array.sort

Esto falla cuando los elementos de la matriz no se implementan #<=>

Nicholaides
fuente
77
No es inteligente, pero comparar to_sety en sizerealidad no hace lo que quieres. Por ejemplo, [a, b, b] coincidiría con [a, a, b]. ¡Salud!
Jo Liss
3
Para aquellos que tropezaron aquí preguntándose lo contrario: el orden debería ser el mismo . Use el eqmarcador, por ejemploexpect([1, 2]).to_not eq([2, 1])
Dennis

Respuestas:

263

Tratar array.should =~ another_array

La mejor documentación sobre esto que puedo encontrar es el código en sí, que está aquí .

x1a4
fuente
Esto no tiene en cuenta el orden, por lo que esta no es una respuesta aceptable, ¿verdad? Presupuesto de los documentos : Passes if actual contains all of the expected regardless of order..
Joshua Muheim
16
Título de esta publicación: "Rspec:" array.should == another_array ”pero sin preocuparse por el orden"
x1a4
3
Esto ahora está documentado oficialmente bajo los criterios del operador
Kelvin
77
Si está utilizando la nueva sintaxis "esperar" que se encuentra en rspec 3.0, vea la respuesta de @JoshKovach.
clozach
46
La sintaxis de Rspec 3.0 está en expect([1, 2, 3]).to match_array([2, 1, 3]): stackoverflow.com/a/19436763/33226
Gavin Miller
238

Desde RSpec 2.11 también puede usar match_array.

array.should match_array(another_array)

Lo que podría ser más legible en algunos casos.

[1, 2, 3].should =~ [2, 3, 1]
# vs
[1, 2, 3].should match_array([2, 3, 1])
Valentin Nemcev
fuente
8
sí, acabo de actualizar a los rieles 4 y = ~ dejó de funcionar donde match_array funciona bien, ¡gracias!
opsb
2
No sé si eso es más legible. Ahora parece que debería ser una coincidencia exacta, pero no lo es. El garabato anterior fue lo suficientemente vago como para no significar nada para una matriz, por lo que no tenía la preconcepción. Tal vez solo soy yo.
Trejkaz
1
Para "exacto", siempre lo has hecho eq(), así que supongo que match_array()es lo suficientemente vago para mí.
awendt
Esto no funciona para mí en las listas ordenadas. Piensa que las listas, que tienen los mismos elementos en un orden diferente, son las mismas. :-(
djangofan
FWIW match_arrayes un alias para contain_exactly( documentación )
Ruy Diaz
136

He encontrado =~que es impredecible y ha fallado sin razón aparente. Pasado 2.14, probablemente deberías usar

expect([1, 2, 3]).to match_array([2, 3, 1])
Josh Kovach
fuente
17
También pasada válida 3.0: expect([1, 2, 3]).to contain_exactly(2, 3, 1). relishapp.com/rspec/rspec-expectations/v/3-0/docs/…
clozach
13

Use match_array, que toma otra matriz como argumento, o contain_exactly, que toma cada elemento como un argumento separado, y a veces es útil para facilitar la lectura. ( documentos )

Ejemplos:

expect([1, 2, 3]).to match_array [3, 2, 1]

o

expect([1, 2, 3]).to contain_exactly 3, 2, 1
Nicholaides
fuente
3

Para el uso de RSpec 3 contain_exactly:

Ver https://relishapp.com/rspec/rspec-expectations/v/3-2/docs/built-in-matchers/contain-exactly-matcher para más detalles, pero aquí hay un extracto:

El emparejador contiene_exactamente proporciona una forma de probar matrices entre sí de una manera que ignora las diferencias en el orden entre la matriz real y la esperada. Por ejemplo:

    expect([1, 2, 3]).to    contain_exactly(2, 3, 1) # pass
    expect([:a, :c, :b]).to contain_exactly(:a, :c ) # fail

Como otros han señalado, si desea afirmar lo contrario, que las matrices deben coincidir tanto con el contenido como con el orden, luego use eq, es decir:

    expect([1, 2, 3]).to    eq([1, 2, 3]) # pass
    expect([1, 2, 3]).to    eq([2, 3, 1]) # fail
Tim Diggins
fuente
1

no documentado muy bien pero agregué enlaces de todos modos:

Rspec3 docs

expect(actual).to eq(expected)


Rspec2 docs

expect([1, 2, 3]).to match_array([2, 3, 1])

Blair Anderson
fuente
12
Ambos esperan (actual) .to eq (esperado) y esperan (actual) .to match_array (esperado) funcionan en rspec3, pero están haciendo cosas diferentes. #match_array ignora el orden, mientras que #eq no.
gucki
1
Esto no responde a la pregunta, ya que el OP solicitó específicamente ignorar la orden.
NobodysNightmare
¡Si! Esto funcionó para mí. Una comparación que falla si el orden de los elementos no es el mismo. ¡Gracias! Me refiero al .to eqmétodo, no al match_array.
djangofan