Resumen: El sujeto de RSpec es una variable especial que se refiere al objeto que se está probando. Las expectativas se pueden establecer implícitamente, lo que admite ejemplos de una línea. Es claro para el lector en algunos casos idiomáticos, pero por lo demás es difícil de entender y debe evitarse. Las let
variables de RSpec son simplemente variables instanciadas (memorizadas) de manera perezosa. No son tan difíciles de seguir como el tema, pero aún pueden dar lugar a pruebas enredadas, por lo que deben usarse con discreción.
El tema
Cómo funciona
El sujeto es el objeto que se está probando. RSpec tiene una idea explícita del tema. Puede estar definido o no. Si es así, RSpec puede llamar a métodos sin hacer referencia a él explícitamente.
De forma predeterminada, si el primer argumento de un grupo ( describe
o context
bloque) de ejemplo más externo es una clase, RSpec crea una instancia de esa clase y la asigna al sujeto. Por ejemplo, los siguientes pases:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
Puede definir el tema usted mismo con subject
:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Puede darle un nombre al tema cuando lo defina:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
Incluso si nombra el tema, aún puede referirse a él de forma anónima:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Puede definir más de un tema con nombre. El sujeto nombrado definido más recientemente es el anónimo subject
.
Independientemente de cómo se defina el tema,
Se crea una instancia perezosamente. Es decir, la instanciación implícita de la clase descrita o la ejecución del bloque pasado subject
no ocurre hasta que subject
o se hace referencia al sujeto nombrado en un ejemplo. Si desea que su sujeto explícito sea instanciado con entusiasmo (antes de que se ejecute un ejemplo en su grupo), diga en subject!
lugar de subject
.
Las expectativas se pueden establecer implícitamente (sin escribir subject
ni el nombre de un sujeto con nombre):
describe A do
it { is_expected.to be_an(A) }
end
El tema existe para apoyar esta sintaxis de una línea.
Cuando usarlo
Un implícito subject
(inferido del grupo de ejemplo) es difícil de entender porque
- Está instanciado detrás de escena.
- Ya sea que se use implícitamente (llamando
is_expected
sin un receptor explícito) o explícitamente (como subject
), no le da al lector información sobre el rol o la naturaleza del objeto en el que se llama a la expectativa.
- La sintaxis de ejemplo de una sola línea no tiene una descripción de ejemplo (el argumento de cadena
it
en la sintaxis de ejemplo normal), por lo que la única información que tiene el lector sobre el propósito del ejemplo es la expectativa misma.
Por lo tanto, solo es útil usar un tema implícito cuando es probable que todos los lectores comprendan bien el contexto y realmente no haya necesidad de una descripción de ejemplo . El caso canónico es probar las validaciones de ActiveRecord con comparadores que deberían:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
Un anónimo explícito subject
(definido con subject
sin nombre) es un poco mejor, porque el lector puede ver cómo se instancia, pero
- todavía puede poner la instanciación del sujeto lejos de donde se usa (por ejemplo, en la parte superior de un grupo de ejemplo con muchos ejemplos que lo usan), lo cual aún es difícil de seguir, y
- tiene los otros problemas que tiene el sujeto implícito.
Un sujeto con nombre proporciona un nombre que revela la intención, pero la única razón para usar un sujeto con nombre en lugar de una let
variable es si desea usar el sujeto anónimo alguna vez, y acabamos de explicar por qué el sujeto anónimo es difícil de entender.
Por lo tanto, los usos legítimos de un subject
sujeto explícito anónimo o con nombre son muy raros .
let
variables
Cómo trabajan ellos
let
las variables son como sujetos nombrados excepto por dos diferencias:
- están definidos con
let
/ en let!
lugar de subject
/subject!
- no establecen lo anónimo
subject
ni permiten que se invoquen expectativas implícitamente.
Cuando usarlos
Es completamente legítimo usarlo let
para reducir la duplicación entre ejemplos. Sin embargo, hágalo solo cuando no sacrifique la claridad de la prueba. El momento más seguro para usar let
es cuando el let
propósito de la variable está completamente claro a partir de su nombre (para que el lector no tenga que encontrar la definición, que podría estar a muchas líneas de distancia, para entender cada ejemplo) y se usa de la misma manera. en cada ejemplo. Si alguna de esas cosas no es cierta, considere la posibilidad de definir el objeto en una variable local antigua simple o llamar a un método de fábrica en el ejemplo.
let!
es arriesgado, porque no es perezoso. Si alguien agrega un ejemplo al grupo de ejemplos que contiene el let!
, pero el ejemplo no necesita la let!
variable,
- Ese ejemplo será difícil de entender, porque el lector verá la
let!
variable y se preguntará si afecta al ejemplo y cómo lo hace.
- el ejemplo será más lento de lo necesario, debido al tiempo necesario para crear la
let!
variablle
Por lo tanto let!
, use , si es que lo usa , solo en grupos de ejemplo pequeños y simples donde es menos probable que los futuros escritores de ejemplos caigan en esa trampa.
El fetiche de una sola expectativa por ejemplo
Existe un uso excesivo común de temas o let
variables que vale la pena discutir por separado. A algunas personas les gusta usarlos así:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(Este es un ejemplo simple de un método que devuelve un número para el que necesitamos dos expectativas, pero este estilo puede tener muchos más ejemplos / expectativas si el método devuelve un valor más complicado que necesita muchas expectativas y / o tiene muchos efectos secundarios que todos necesitan expectativas.)
Las personas hacen esto porque han escuchado que uno debe tener solo una expectativa por ejemplo (que se confunde con la regla válida de que solo se debe probar una llamada a un método por ejemplo) o porque están enamorados de la astucia de RSpec. ¡No lo hagas, ya sea con un sujeto anónimo o nombrado o con una let
variable! Este estilo tiene varios problemas:
- El sujeto anónimo no es el tema de los ejemplos, el método es el tema. Escribir el examen de esta manera arruina el idioma, lo que hace que sea más difícil pensar en ello.
- Como siempre ocurre con los ejemplos de una línea, no hay espacio para explicar el significado de las expectativas.
- Hay que construir el tema para cada ejemplo, lo cual es lento.
En su lugar, escribe un solo ejemplo:
describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end
:aggregate_failures
etiqueta en una línea comoit "marks a task complete", :aggregate_failures do
(tomada del libro Rails 5 Test Prescriptions)expect(result).to be_between(0, 9)
.expect(result.to_s).to match(/^[0-9]$/)
; sé que es feo, pero realmente prueba lo que está diciendo, o tal vez, usebetween
+is_a? Integer
, pero aquí está probando el tipo también. Y simplementelet
... no debería ser motivo de preocupación, y en realidad puede ser mejor reevaluar los valores entre ejemplos. De lo contrario +1 para el puestolet!
y no he sido para convencer a mis compañeros por mi cuenta. Voy a enviar esta respuesta.Subject
ylet
son solo herramientas para ayudarlo a ordenar y acelerar sus pruebas. La gente de la comunidad rspec los usa, así que no me preocuparía si está bien usarlos o no. Se pueden usar de manera similar pero tienen propósitos ligeramente diferentes.Subject
le permite declarar un sujeto de prueba y luego reutilizarlo para cualquier número de casos de prueba siguientes. Esto reduce la repetición del código (SECANDO su código)Let
es una alternativa a losbefore: each
bloques, que asignan datos de prueba a variables de instancia.Let
te da un par de ventajas. Primero, almacena en caché el valor sin asignarlo a una variable de instancia. En segundo lugar, se evalúa de forma perezosa, lo que significa que no se evalúa hasta que una especificación lo solicita. Por lo tanto, lelet
ayuda a acelerar sus pruebas. También creo quelet
es más fácil de leerfuente
subject
es lo que se está probando, generalmente una instancia o una clase.let
es para asignar variables en sus pruebas, que se evalúan de forma perezosa en comparación con el uso de variables de instancia. Hay algunos buenos ejemplos en este hilo.https://github.com/reachlocal/rspec-style-guide/issues/6
fuente