Domanda

Prima di tutto lo so che è cattiva pratica avere più asserzioni nel test unitario.

Ma a volte è necessario testare una transazione atomica.Come esempio semplificato, prendiamo qualche applicazione bancaria che ha la classe dell'account:

class Account 
  attr_accessor :balance

  def transfer(to_account, amount)
    self.balance -= amount
    to_account.balance += amount
    Audit.create(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
  end

end
.

In questa situazione voglio controllare 3 cose insieme:

    .
  1. Saldo del conto sorgente è diminuito da amount
  2. Saldo del conto di destinazione è aumentato di amount
  3. Il record di audit è inserito
  4. Qual è il modo migliore per testare il metodo @account.transfer?

È stato utile?

Soluzione

In questa situazione voglio controllare 3 cose insieme:

Sostenerei che ciò che vuoi veramente è descrivere il comportamento di queste cose in determinate condizioni, e quindi assicurarti che il comportamento soddisfi le tue specifiche. Questo potrebbe significare che le cose accadono insieme; Oppure potrebbe significare che alcune cose accadono solo in una serie di condizioni e non degli altri, o che un'eccezione causa tutto per essere tornato al suo stato originale.

Non c'è la magia per avere tutte le tue asserzioni in un test, tranne per rendere le cose più velocemente. A meno che tu non stia affrontando una grave penalità di prestazione (come spesso accade in test di pieno pila), è molto meglio usare un'asserzione per test.

RSPEC lo rende semplice estrarre la fase di impostazione del test in modo che venga ripetuta per ogni esempio:

class Account 
  attr_accessor :balance

  def transfer(to_account, amount)
    self.debit!(amount)
    to_account.credit!(amount)
    Audit.create!(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
  rescue SomethingBadError
    # undo all of our hard work
  end

end

describe Account do
  context "when a transfer is made to another account" do
    let(:other_account} { other_account }
    context "and the subject account has sufficient funds" do
      subject { account_with_beaucoup_bucks }
      it "debits the subject account"
      it "credits the other account"
      it "creates an Audit entry"
    end
    context "and the subject account is overdrawn" do
      subject { overdrawn_account }
      it "does not debit the subject account"
      it "does not credit the other account"
      it "creates an Audit entry" # to show the attempted transfer failed
    end
  end
end
.

Se tutti e tre i test nel "sentiero felice" sono passati, quindi tutti "è successo insieme", poiché lo stato del sistema iniziale era lo stesso in ogni caso.

Ma devi anche assicurarti che le cose non siano accadono quando qualcosa va storto e che il sistema torna al suo stato originale. Avere più affermazioni rende facile vedere che funziona come previsto, e quando i test falliscono, esattamente come hanno fallito.

Altri suggerimenti

Asserzioni multiple per test non sono sempre una brutta pratica. Se le più asserzioni verificano lo stesso comportamento, non ci sono problemi con esso. Il problema esiste quando si tenta di verificare più di un comportamento nello stesso test. Naturalmente ci sono alcuni rischi con più asserzioni per test. Uno di questi è possibile lasciare accidentalmente valori da un precedente set di test che invalida un test precedente in modo strano. Inoltre, quando un assert è falso, tutta l'altra sinistra non verrà eseguita, il che può causare difficoltà a capire cosa c'è di andare avanti. Ma essere ragionevole, puoi avere più asserzioni che asseriscono lo stesso comportamento, preferibilmente brevi e senza configurazione extra.

Nel caso semplice che hai portato, userei più asserzioni, perché è così semplice. Ma ovviamente può ottenere molto più complicato, come il saldo negativo, diversi tipi di conti e cose. Allora sarebbe meglio usare diversi test con un (preferibilmente) affermano. Lo avrei organizzato in questo modo:

    .
  • 1 Per testare il comportamento dell'account corrente (caso più semplice);
  • 1 ad ogni diverso percorso il metodo può avere (eccezioni, negativo equilibrio ecc.);
  • 1 per testare l'audit in ogni queste possibilità;

  • 1 per testare il comportamento della corrente a_ccount (caso più semplice);

  • 1 ad ogni diverso percorso il metodo può avere. (Eccezioni, negative equilibrio ecc.);
  • 1 per testare l'audit in ogni di queste possibilità;

Poiché il test di audit è piuttosto semplice e non richiede alcuna configurazione extra, è anche possibile testarla insieme con account e to_account.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top