2011-02-28 18 views
22

Tengo una confusión al implementar Resque en paralelo con los ejemplos de Rspec. La siguiente es una clase con método costoso .generate(self) clase SomeClass ... ChangeGenerator.generate (auto) ... final¿Cómo puentear las pruebas usando Resque con ejemplos de Rspec?

Después de implementar resque, la clase anterior cambió a la siguiente y se añade una clase ChangeRecorderJob .

class SomeClass 
    ... 
    Resque.enqueue(ChangeRecorderJob, self.id) 
    ... 
end 

class ChangeRecorderJob 
    @queue = :change_recorder_job 

    def self.perform(noti_id) 
    notification = Notification.find(noti_id)  
    ChangeGenerator.generate(notification) 
    end 
end 

Funciona perfectamente. Pero tengo 2 preocupaciones.

Antes, mi espec. De ejemplo se usa para probar la pila entera del método .generate(self). Pero ahora, desde que introduje eso en el trabajo de Resque, ¿cómo puedo unir mis ejemplos para hacer que la misma prueba sea verde sin aislar? ¿O tengo que aislar la prueba?

Y, por último, si tengo 10 trabajos para enque, ¿tengo que crear 10 clases de trabajo separadas con el método self.perform?

Respuesta

32

Probar cosas asíncronas como esta siempre es complicado. Lo que hacemos es:

  • En nuestras pruebas funcionales nos aseguramos de que el trabajo se ponga en cola. Usar mocha o algo similar con una expectativa es normalmente suficiente. Si desea ejecutar un servidor de prueba redis, puede verificar que creció la cola correcta y que los parámetros del trabajo son correctos. Aunque estás probando Resque un poco en ese punto.

  • Los trabajos se prueban de forma aislada como pruebas unitarias. Como solo tienen un método de clase llamado perform, las pruebas de su unidad son bastante simples. En su caso, usted probaría que ChangeRecorderJob.perform hace lo que quiere. Tendemos a probar que los trabajos están en la cola apropiada, que los parámetros del trabajo son válidos y que el trabajo hace lo que queremos.

  • Ahora, probar todo junto es la parte difícil. He hecho esto de dos maneras diferentes y cada uno tiene ventajas y desventajas:

    • mono-parche Resqueue.enqueue para ejecutar la tarea de forma sincrónica A partir de 1.14.0 resque puede utilizar Resque.inline = true en su inicializador en lugar de mono-parches
    • simular un trabajador que hace estallar un trabajo de la cola y en realidad se ejecuta en un proceso bifurcado

Ejecución del trabajo synchronou astuto es de lejos el más fácil de los dos. Usted acaba de cargar algo así como lo siguiente en su spec_helper:

 
module Resque 
    alias_method :enqueue_async, :enqueue 

    def self.enqueue(klass, *args) 
    klass.new(0, *args).perform 
    end 
end 

A partir de 1.14.0 resque que sólo puede establecer Resque.inline = true en su inicializador en lugar de mono-aplicación de parches. Si estás atascado en una versión anterior de resque, el parche de mono es necesario.

Tenga en cuenta que debido a que está ejecutando sincrónicamente aquí, incurrirá en el costo de su trabajo de larga duración. Quizás lo más importante es que se ejecutará en el mismo proceso, por lo que no es una representación completamente precisa de cómo se ejecutará su trabajo.

para ejecutar el trabajo de un trabajador en forma de horquilla, al igual que resque haría, tendrá que hacer algo como lo siguiente:

 
def run_resque_job(job_class, job_args, opts={}) 
    queue = opts[:queue] || "test_queue" 

    Resque::Job.create(queue, job_class, *job_args) 
    worker = Resque::Worker.new(queue) 
    worker.very_verbose = true if opts[:verbose] 

    if opts[:fork] 
    # do a single job then shutdown 
    def worker.done_working 
     super 
     shutdown 
    end 
    worker.work(0.01) 
    else 
    job = worker.reserve 
    worker.perform(job) 
    end 
end 

Hay un ligero retraso en conseguir el trabajador para que aparezca el trabajo de la cola. Y, naturalmente, tendrá que ejecutar un servidor de prueba redis para que el trabajador tenga una cola para salir.

Estoy seguro de que otras personas han ideado maneras ingeniosas de probar trabajos de resquebrajamiento. Esto es lo que ha estado funcionando para mí.

+0

Voy a tratar estas formas y veo que funciona para mí. – Autodidact

+3

El 'Resque.inline = true' es una solución inteligente, pero como ha señalado puede hacer que las cosas se vuelvan lentas y he encontrado casos en algún código descuidado en el que ejecutarlo de forma síncrona tuvo resultados diferentes que las ejecuciones asincrónicas. Sería genial si Resque tuviera un ayudante de prueba donde pudieras hacer llamadas como 'Resque.last_job',' Resque.process! ', Y' Resque.clear! '. –

1

Tendrá que hacer dos pruebas diferentes. Uno para ingresar, para asegurarse de que los trabajos se pongan en cola en las colas Resque y el segundo para asegurarse de que los trabajos en la cola cuando los recogen los trabajadores se realicen según sus requisitos.

No, no necesita escribir 10 métodos de ejecución diferentes. Cuando ejecuta trabajadores de Resque, recogen los trabajos de la cola y llaman ciegamente el método .perform en su trabajo. Por lo tanto, se espera que su trabajo tenga el método de ejecución.

+0

Acerca del método de ejecución múltiple, creo que no pude aclararlo. Digamos que tengo 3 clases de trabajos con un solo método '.perform' en cada uno. Estos 3 trabajos son para el mismo modelo. Entonces, en lugar de escribir 3 clases de trabajo separadas, ¿cómo escribir estos 3 '.perform' en una sola clase de trabajo? – Autodidact

+0

El método 'perform' toma varargs. Podría agregar un valor discriminador si así lo deseara y luego su único 'desempeño' podría simplemente enviarse a cualquier método que esté proporcionando la funcionalidad que necesita. – nirvdrum

+0

Gracias por aligerarme. – Autodidact

8

Utilice resque_spec para probar la unidad.

describe "#recalculate" do 
    before do 
    ResqueSpec.reset! 
    end 

    it "adds person.calculate to the Person queue" do 
    person.recalculate 
    Person.should have_queued(person.id, :calculate).in(:people) 
    end 
end 

Y para sus pruebas de integración:

describe "#score!" do 
    before do 
    ResqueSpec.reset! 
    end 

    it "increases the score" do 
    with_resque do 
     game.score! 
    end 
    game.score.should == 10 
    end 
end 
Cuestiones relacionadas