Visualizzazione post con etichetta howTo. Mostra tutti i post
Visualizzazione post con etichetta howTo. Mostra tutti i post

giovedì 12 settembre 2013

Il DBB per il test della componente "Server" - parte 2


Prosecuzione del post sul BDD per il test del server

Il BDD - behavior-driven development (continua)

Definito lo scenario con cucumber, come abbiamo fatto nel post precedente, è necessario definire i test di validazione delle features, che si concentreranno sul "consumo" dei webservice e non sulla tecnica di programmazione.

I punti focali di questi test sono che:
  1. possiamo farli prima di iniziare a (o a far) codificare
  2. sono indipendenti dal linguaggio con cui sarà realizzato il progetto.
Per la definizione degli step possiamo partire dagli snippet generati automaticamente da cucumber (sul sito vi sono vari tutoriarl che illustrano come codificare gli step).
In questo caso, volendo scrivere un test di controllo su come un client esterno possa consumare i servizi esposti, non verrà verificata la logica dei modelli, ma il funzionamento dei controller. 

Ad esempio nel nostro case avremo:


Given(/^a user "(.*?)" with password "(.*?)"$/) do |user, password|
 @username=user
 @password= password
end

When(/^he logs whit username and password$/) do
  header 'Accept', 'application/json' 
  header 'Content-Type', 'application/json'
  params={username: @username, password: @password}
  post 'users/sign_in', params.to_json
  page = JSON.parse(last_response.body)
  @token=page["auth_token"]
  @token.should_not == nil
end

Then(/^he should navigate page "(.*?)" and see (\d+) records$/) do |arg1, arg2|
  get arg1, {auth_token: @token}
  page = JSON.parse(last_response.body)
  page.size.should == arg2.to_i
end
 
Then(/^he should navigate page "(.*?)"$/) do |arg1|
  get arg1, {auth_token: @token}
end

Then(/^he should see olny his documents$/) do 
  get "my_documents", {auth_token: @token}
  last_response.status.should == 200 #HTTP status OK
  page = JSON.parse(last_response.body)
  page.map{|u| u["user"]["customer_name"]}.compact.uniq.should include(@user.customer_name)
end

Then(/^he shouldn't navigate page "(.*?)"$/) do |arg1|
   get arg1, {auth_token: @token}
   last_response.status.should == 403 #HTTP status forbidden
end

Questo test verificherà - indipendentemente dalla modalità di realizzazione - che il nostro server risponda a: 
- un servizio di login
- un servizio di risposta agli indirizzi customers, operators, settings, documents, che verifichi il tocken ed il profilo dell'untente , eventualmente restituendo uno stato di fobidden e che restituisca una array di record
- un servizio denominato "my_documents"

Naturalmente alcuni di questi test, come ad esempio l'autenticazione con username e password, presuppongono che il sistema sia stato popolato. Basta predisporre un passo di popolazione dell'ambiante di test, attraverso una delle 2 modalità che preferiamo (ed in funzione di cosa dobbiamo testare):

A) utilizzo degli script di avvio di cucumber, in particolare dello svript env.rb, in cui, in questo caso ho aggiunto il blocco
    Before do
      User.create(:username=>"operator1", :password=>"test1234", :role=>"operator", :email=>"operator1@mail.local")#this code is run before each scenario
      User.create(:username=>"operator2", :password=>"test1234", :role=>"operator", :email=>"operator2@mail.local")#this code is run before each scenario
      User.create(:username=>"operator3", :password=>"test1234", :role=>"operator", :email=>"operator3@mail.local")#this code is run before each scenario
      cust1=User.create(:username=>"customer1", :password=>"test1234", :role=>"customer", :customer_name=>"azienda1", :email=>"customer1@mail.local")#this code is run before each scenario
      cust2=User.create(:username=>"customer2", :password=>"test1234", :role=>"customer", :customer_name=>"azienda2", :email=>"customer2@mail.local")#this code is run before each scenario
      cust1.documents.create(document_type: "F24_init")
      cust1.documents.create(document_type: "Bilancio_init")
      cust2.documents.create(document_type: "F24_init")
      cust2.documents.create(document_type: "Bilancio_init")
    end

B) scrittura di scenari di popolazione, che utilizzeremo per i test sui servizi.


Abbiamo quindi simulato - evitando di farlo manualmente ogni volta -
- l'apertura della finestra di login
- l'inserimento di user e password "amministrative"
- la navigazione (manuale, mancando al momento i link) di 4 pagine e il conteggio de record
- il logout
- l'inserimento di user e password "utente"
- la navigazione (manuale, mancando al momento i link) di 5 pagine e il conteggio de record

Considerando che al progredire di un progetto i dati da immettere per verificare quanto abbiamo codificato aumentano esponenzialmente... siamo ancora sicuri che scrivere test in questo modo sia una perdita di tempo?


lunedì 9 settembre 2013

Paradigma Client/Server anche per sviluppare applicazioni "semplici"? Perchè no?

Oggi ho iniziato a sviluppare una piccola applicazione - rigorosamente con Rails -  ad uso interno.

Nulla di complicato, anzi tutt'altro, ma mi sono chiesto se la sua semplicità fosse un buon motivo per non svilupparla "bene", ovvero per non dividere i livelli di logica di business e di presentazione.

Non è che le cose piccole siano per definizioni da fare male, anzi .... la semplicità (così ci insegna la UX) spesso è figlia di molto lavoro ed esperienza.

Veniamo quindi alla nostra applicazione.

Le funzioni richieste sono basilari, e dopo una prima fase di analisi sono riassumibili in:
- deve prevedere accessi profilati ed autenticati
- deve permettere la gestione di settings
- deve permettere la il caricamento di documenti con alcuni metadati (legati agli utenti ed ai setting)
- deve inviare delle notifiche a fronte del caricamento
- deve tracciare le azioni eseguite sul documento

Abbiamo quindi il modello principale DOCUMENT a cui sono funzionali il modello USER ed il modello SETTING

La prima applicazione gestirà la logica di business e le funzioni backend del sistema e sarà interrogabile solo tramite WebService

La seconda applicazione - che potrà anche essere client javascript - consumerà tali servizi e fornirà la User Interface all'utente, definendo la logica di presentazione

I vantaggi - a parte quelli puramente "accademici" - sono molteplici:

1) Possiamo demandare ad un'altro gruppo / persona lo sviluppo dell'interfaccia
2) Possiamo scrivere differenti interfacce per differenti device. Quindi realizzare da un client locale, compilato per la singola piattaforma, ad una webapp, ad un sito mobile
3) Il gruppo che si occupa delle interfacce utente NON POTRA' in alcun modo interferire con la logica di business
4) Nessun codice sarà condiviso tra i due gruppi e pertanto sarà effettivamente possibile DIMEZZARE i tempi di sviluppo (lo so non è vero.. ma passatemi la licenza), a patto di definire bene la struttura dei messaggi

Svantaggi:

1) senza una suite di test chi sviluppa il backend lavorerà alla cieca o dovrà fare dello sviluppo "a perdere".
2) senza una formalizzazione dei messaggi chi lavora al frontend sarà costretto a lavorare senza dati in modo statico

A mio parere questi non sono degli svantaggi, ma una evidente forzatura a costringere lo sviluppatore in termini di ciò che può essere TESTATO e non di ciò che può essere VISTO