Cum am inlocuit zeci de corpuri de testare cu o functie simpla

Totul a inceput cand am adaugat steaguri de functii in aplicatia noastra. Dupa cateva deliberari, am creat un model „set de caracteristici” cu campuri booleene pentru fiecare caracteristica:

class FeatureSet (models.Model):



name = models.CharField (max_length = 50)



can_pay_with_credit_card = models.BooleanField ()



can_save_credit_card = models.BooleanField ()



can_receive_email_notifications = models. matrimoniale public 24 BooleanField ()

Am adaugat o cheie straina din contul de utilizator la modelul seturilor de caracteristici si am creat seturi de caracteristici pentru utilizatorii „pro”, „newbie” si „comercial”.

Pentru a aplica caracteristicile, am adaugat teste in locuri adecvate. De exemplu:

def pay_with_credit_card (self, user_account, amount):



daca nu user_account.feature_set.can_pay_with_credit_card:



ridicati FeatureDisabled (‘can_pay_with_credit_card’)




.. porn curve .

In acest moment am avut o baza de cod mare si o multime de teste. Din pacate, multe dintre testele noastre au fost o relicva de pe vremea cand foloseam pe scara larga corpurile de iluminat.

Gandul ca trebuie sa actualizam si sa adaugam noi dispozitive a fost inacceptabil. Dar, totusi, a trebuit sa testam noile caracteristici, asa ca am inceput sa scriem teste de genul acesta:

def test_should_charge_credit_card (self):



feature_set = user_account.feature_set feature_set. dame de companie din maramures can_pay_with_credit_card



= True



feature_set.save (update_fields = [‘can_pay_with_credit_card’])




pay_with_credit_card (user_account, 100)

def test_should_fail_when_feature_disabled (self):



feature_set = user_account.feature_set feature_set.can_pay_with_credit_card



= Fals




cu self.assertRaises (FeatureDisabled):



pay_with_credit_card (self.user_account, 100)

Am avut o multime de teste de actualizat si unele dintre caracteristicile pe care le-am adaugat au intrerupt fluxul altor teste care au dus la o mizerie!

Am folosit deja manageri de context pentru a ne imbunatati testele in trecut si am crezut ca putem folosi unul aici pentru a activa si dezactiva caracteristicile:

din contextlib import contextmanager @ contextmanager



def feature (feature_set, feature_name, enabled):



original_value = getattr (feature_set,



feature_name ) setattr (feature_set, feature_name, enabled)



feature_set. curve cu tate save (update_fields = [feature_name]) try:



cedeaza in cele din urma:



setattr (feature_set , feature_name, original_value)



feature_set.save (update_fields = [feature_name])

Ce face acest manager de context?

  1. Salvati valoarea initiala a caracteristicii.
  2. Setati noua valoare pentru caracteristica.
  3. Randamente – aici se executa de fapt codul nostru de testare.
  4. Setati caracteristica inapoi la valoarea initiala

Acest lucru a facut testele noastre mult mai elegante:

def test_should_charge_credit_card (auto):



cu caracteristica (



user_account.feature_set,



can_pay_with_credit_card,



Adevarat,



):




pay_with_credit_card (USER_ACCOUNT, 100) def test_should_fail_when_feature_disabled (auto):



cu caracteristica (



user_account. dame de companie timis feature_set,



can_pay_with_credit_card,



False



):




cu self.assertRaises (FeatureDisabled ):



pay_with_credit_card (self.user_account, 100)

Acesta a fost un mare pas inainte, dar inca nu am fost multumiti.

Acest manager de context s-a dovedit a fi foarte util pentru caracteristici, asa ca ne-am gandit … publi24 matrimoniale timis de ce sa nu-l folosim si pentru alte lucruri?

Am avut o multime de metode care implica mai multe caracteristici:

def test_should_not_send_notification (self):



feature_set = user_account.feature_set



with feature (feature_set, can_pay_with_credit_card, True):



cu caracteristica (feature_set, can_receive_notifications, False):




pay_with_credit_card (user_account, 100)

Sau mai multe obiecte:

def test_should_not_send_notification_to_inactive_user (self):



feature_set = user_account.feature_set



user_account.user.is_active = Fals



cu caracteristica (feature_set, can_receive_notifications, False):



pay_with_credit_card (user_account, 100)

Asa ca am rescris managerul de context pentru a accepta orice obiect si am adaugat suport pentru mai multe argumente:

@contextmanager



def temporar (obj, ** kwargs):



original_values = {k: getattr (obj, k) , pentru k in kwargs} pentru k, v in kwargs.items ():



setattr (obj, k, v) obj. escorte din arad save (update_fields = kwargs.keys ()) try:



cedeaza in cele din urma:



pentru k, v in original_values.items ():



setattr (obj, k, v) obj.save (update_fields = original_values.keys ())

Managerul de context poate accepta acum mai multe functii, poate salva valorile originale, poate seta noile valori si le poate restabili dupa ce am terminat.

Testarea a devenit mult mai usoara:

def test_should_not_send_notification (self):



cu temporar (



user_account. escorte publi24 alba feature_set,



can_pay_with_credit_card = True,



can_receive_notifications = False,



):




pay_with_credit_card (user_account, 100)



self.assertEquals (len (outbox), 0)

Acum putem folosi functia si pe alte obiecte:

def test_should_fail_to_login_inactive_user (self):



cu temporar (user, is_active = False):



response = self.login (user)



self.assertEqual (response.status_code, 400)

Profit!

Dupa o vreme ne-am simtit confortabil cu noul utilitar, am observat un alt beneficiu de performanta.

In testele care au avut setari grele, am reusit sa mutam setarea de la nivelul testului la nivelul clasei. escorte din timisoara

Pentru a ilustra diferenta, sa testam o functie care trimite o factura utilizatorilor. Facturile sunt de obicei trimise numai atunci cand tranzactia este finalizata. Pentru a crea o tranzactie completa avem nevoie de o multime de configurare (alegeti produse, platiti, efectuati plata etc.).

Acesta este un test care necesita multa configurare:

clasa TestSendInvoice (TestCase): def setUp (self):



self.user = User. matrimoniale botosani objects.create_user (…)



self.transaction = Transaction. matrimoniale campina femei create (self.user, …)



Transaction.add_product (. public 24 buc matrimoniale ..)



Transaction.add_product (… escorte caransebes )



Transaction.checkout (…



  • www escorte mures
  • curve zimnicea
  • curve ieftime
  • curve de bucuresti
  • dame de companie simpatie
  • escorte non stop bucuresti
  • nr de tel curve
  • matrimoniale buzau
  • escorte sex iasi
  • fete din galati curve
  • viata unei dame de companie
  • matrimoniale alexandria cu poze
  • publicat 24 matrimoniale brasov
  • curve romania
  • escorte sex anal bucuresti
  • dame de companie crangasi
  • escorte cernavoda
  • matrimoniale campia turzii
  • dame de companie pub24
  • dame de companie bacau deplasare





)



Transaction.request_payment (. curve in bacau ..)



Transaction.process_payment (… koch curve )

def test_should_not_send_invoice_to_commercial_user (self):



self.user.type = ‘commercial’



mail.outbox = []



Transaction.send_invoice (self.user)



self. how to find gradient of a curve assertEqual (len (mail.outbox), 0)

def test_should_attach_special_offer_to_pro_user (self):



self.user.type = ‘pro’



mail.outbox = []



Transaction.send_invoice (self. escorte testate user)



self.assertEqual (len (mail.outbox), 1)



self.assertEqual (



mail.outbox [ 0] .subject,



„Factura si o oferta speciala!”



)

Functia setUp trebuie sa se execute inainte de fiecare functie de test, deoarece functiile de testare modifica obiectele si ar putea crea o dependenta periculoasa intre cazurile de testare. escorte anglia

Pentru a preveni dependentele dintre cazurile de testare, trebuie sa ne asiguram ca fiecare test lasa datele exact asa cum le-a obtinut. Din fericire, exact asta face noul nostru manager de context:

clasa TestSendInvoice (TestCase): @classmethod



def setUpTestData (cls):




cls.user = User.objects.create_user (.. la curve xxx .)



cls.transaction = Transaction.create (cls.user, .. escorte panduri .)



Transaction.add_product (.. .)



Transaction. curve romanesti add_product (…)



Transaction.checkout (.. curve ieftine galati .)



Transaction.request_payment (…)



Transaction. process_payment (…)

def test_should_not_send_invoice_to_commercial_user (self):



mail.outbox = []



cu temporar (self.user, type = ‘commercial’):



Transaction. send_invoice (self.user)



self.assertEqual (len (mail.outbox), 0)

def test_should_attach_special_offer_to_pro_user (self):



mail.outbox = []



cu temporar (self.user, type = ‘pro’):



Transaction. send_invoice (self.user)



self.assertEqual (len (mail.outbox), 1)



self.assertEqual (



mail.outbox [0] . subject,



„Factura si o oferta speciala!”



)

Am mutat codul de configurare in setUpTestData. Codul de configurare se va executa o singura data pentru intreaga clasa de testare, rezultand teste mai rapide.

Motivatia acestui procesor de context a fost lunga noastra relatie nesanatoasa cu corpurile de iluminat. Pe masura ce ne-am redimensionat aplicatia, programele au devenit o povara. Avand atatea teste care se bazeaza pe ele, a fost dificila inlocuirea completa.

Odata cu adaugarea de functii, stiam ca nu mai vrem sa ne bazam pe dispozitive si am cautat modalitati creative, mai detaliate si mai usor de gestionat, de gestionare a datelor de testare. Avem o modalitate simpla de a crea diferite variatii ale unui obiect pentru testare a fost exact ceea ce aveam nevoie.