Rails TDD utilizzando un mailer di terze parti?
-
28-10-2019 - |
Domanda
Ho un'applicazione Rails su cui sto implementando l'API di Twilio SMS e sono un po 'perso su come testare il mio design.
Per iniziare ho appena creato un modello che è un mailer SMS che incapsulerà l'API TWIIO e voglio essere in grado di testarlo e garantire la funzionalità senza utilizzare i crediti SMS o bombardare qualcuno con messaggi di testo di prova.
So come implementare l'API e farla funzionare nel codice, ma ciò con cui ho bisogno di aiuto è effettivamente testare il codice per assicurarsi che funzioni e prevenire la rottura in futuro. Qualcuno potrebbe fornire qualche consiglio?
Grazie!
Soluzione
La mia esperienza con i test e con il test di Twilio Applications, è che tu Test per eliminare il rischio che aggiungi. Ti consigliamo di usare il Twilio Gem Invece di arrotolare il tuo codice SMS rispetto al loro endpoint di riposo: ciò riduce al minimo la quantità di rischio.
Avvolgi l'API nel modo più sottilmente possibile nella tua classe di logica aziendale e prova principalmente la logica aziendale. Ad esempio, nel mio sistema, gli SMS vengono inviati dalla classe di promemoria. Il codice assomiglia a questo:
class SomeWrapperClass
if (RAILS_ENV == "testing")
@@sent_smses = []
cattr_accessor :sent_smses
end
def send_a_message(to, from, message, callback_url = nil)
unless RAILS_ENV == "testing"
Twilio::SMS.message(to, from, message, callback_url)
else
@@sent_smses << {:to => to, :from => from, :message => message, :callback_url => callback_url}
end
end
end
Questo mi permette di scrivere test incentrati sulla mia logica aziendale, che è la roba che sto per rovinare. Ad esempio, se voglio testare un metodo Send_reminder (client) che invia un messaggio SMS:
test "sends reminder to client" do
SomeWrapperClass.sent_smses = []
client = clients(:send_reminder_test_case)
Reminder.send_reminder(client)
sent_message = SomeWrapperClass.sent_smses.last
assert !sent_message.blank?, "Sending a reminder should fire an SMS to client."
assert sent_message.index(client.name) >= 0, "Sending a reminder should fire an SMS with the client's name in it.
...
end
Ora sto testando il rischio effettivo che ho aggiunto, ovvero che sto rovinando il promemoria.send_reminder. L'involucro, d'altra parte, dovrebbe essere vicino al rischio.
Altri suggerimenti
Potresti usare il mio gem Twilio.rb, che è già testato, e poi deriderlo nei test, ad esempio con Mocha
Twilio::SMS.expects(:create).with :to => '+19175551234', :from => '+12125551234', :body => 'this is easy!'
I test unitari non dovrebbero mai colpire i servizi esterni, dovrebbero sempre essere derisi. Ciò è segue da un principio generale dei test unitari secondo cui i test non dovrebbero estendere il confine di classe dell'oggetto da testare e che gli oggetti collaborativi devono essere derisi/scompostati.
Spero che sia di aiuto!
Ovviamente separare quanta più logica possibile. In questo modo puoi testare tutto il resto il più possibile e quindi lasciare solo le chiamate all'API esterna che necessitano di test.
Lavorare con API esterne può essere complicato. Un'opzione è quella di deridere la risposta a qualcosa che sai che funzionerà per te o alla risposta che ti aspetteresti, questo può ovviamente essere un po 'fragile però. Un'altra opzione è guardare qualcosa di simile VCR. Questo registrerà una volta la chiamata all'API esterna e la riprodurrà ogni volta che la chiami di nuovo.
Questo ragazzo sembra aver iniziato a risolvere il tuo problema: https://github.com/arfrank/fake-twilio-pi
Probabilmente non hai bisogno di testare il codice di Twiliolib, ma se non vuoi stupirsi i metodi di Twiliolib potresti usare la gemma falsaweb, in cui si definisce la risposta per le richieste specificate.
Simile a Steve menzionato, ho eliminato la richiesta con Mocha:
# In Twilio initializer
TWILIO_ACCOUNT = Twilio::RestAccount.new(TWILIO_CONFIG[:sid], TWILIO_CONFIG[:token])
# In a test helper file somewhere
class ActiveSupport::TestCase
# Call this whenever you need to test twilio requests
def stub_twilio_requests
# Stub the actual request to Twilio
TWILIO_ACCOUNT.stubs(:request).returns(Net::HTTPSuccess.new(nil, nil, nil).tap { |n|
n.stubs(:body).returns("<?xml version=\"1.0\"?>\n<TwilioResponse></TwilioResponse>\n")
})
end
end