#11 Refactoring User Name Part 2
Nel precente episodio abbiamo parlato di refactoring e di come possiamo spostare il codice dalla vista a un metodo del modello al fine di eliminare le duplicazioni di codice. Il codice nella nostra classe di modello User
potrebbe essere migliorato ulteriormente.
class User < ActiveRecord::Base def full_name name = first_name + ' ' name += "#{middle_initial}. " unless middle_initial.nil? name += last_name end end
La classe di modello User
come l’abbiamo lasciata al termine della scorsa puntata.
Testing
Prima di iniziare il refactoring della clase di modello User
, guardiamo ai test. I test e il refactoring vanno di pari passo, dal momento che l'intento del refactoring è quello di migliorare il codice senza cambiarne il comportamento. Una buona copertura con i test garantisce che il nostro refactoring non intacchi ciò che il codice fa in origine correttamente. Quando un modello viene creato in una applicazione Rails, il file di test per tale modello viene automaticamente creato dal framework. Diamo un’occhiata a user_test.rb
nella cartella /test/unit
1.
require 'test_helper' class UserTest < ActiveSupport::TestCase # Replace this with your real tests. test "the truth" do assert true end end
Il test di default fornito da Rails.
C’è un test di default fornito dal codice sopra che controlla semplicemente che true == true
. Sostituiremo questo codice con il primo test fatto da noi, creando un nuovo utente senza una iniziale del secondo nome e controllando che sia restituito il nome completo che ci aspettiamo.
test "full name without middle initial" do user = User.new(:first_name => "John", :last_name => "Smith") assert_equal 'John Smith', user.full_name end
Il nostro primo test controlla un nome privo di iniziale del secondo nome.
Per lanciare i nostri test potremmo eseguire il comando rake test
dalla console dalla cartella della nostra applicazione, ma in realtà useremo AutoTest2. Quest’ultimo può essere installato installando il gem ZenTest (sudo gem install ZenTest
). AutoTest ha il vantaggio che lancerà in continuazione i nostri test, in modo tale che sarà facile vedere quando avremo rotto qualcosa con del nuovo codice. Lanceremo autotest
e vedremo quali risultati otterremo.
asalicetti$ autotest loading autotest/rails /usr/local/bin/ruby -I.:lib:test -rtest/unit -e "%w[test/functional/users_controller_test.rb test/unit/user_test.rb].each { |f| require f }" | unit_diff -u Loaded suite -e Started . Finished in 0.046945 seconds. 1 tests, 1 assertions, 0 failures, 0 errors
Il risultato restituito da AutoTest
Il nostro primo test ha avuto successo! Ora creeremo un altro test per verificare un nome con l’iniziale del cognome, aggiungendo il metodo qui sotto alla nostra classe UserTest
.
test "full name with middle initial" do user = User.new(:first_name => "Paul", :middle_initial => "P", :last_name => "Hughes") assert_equal 'Paul P. Hughes', user.full_name end
Quando salverete il file di test, AutoTest dovrebbe automaticamente rilanciare i test. Entrambi i test dovrebbero ora avere successo.
Refactoring
Ora che sappiamo che il nostro metodo full_name
si comporta come ci aspettavamo, possiamo cominciare a rivederne il codice. L’utilizzo della valiabile locale nel metodo e la concatenazione di stringhe sembrano non neccessari, per cui li rimuoveremo. Potremmo piuttosto mettere ogni parte del nome dell’utente in un array e unirle separandole con degli spazi.
class User < ActiveRecord::Base def full_name [first_name, middle_initial, last_name].join(' ') end end
Prima revisione della classe User
.
Al salvataggio del file, i test dovrebbero partire in automatico e potremo vedere che falliranno entrambi.
1) Failure: test_full_name_with_middle_initial(UserTest) [./test/unit/user_test.rb:11:in `test_full_name_with_middle_initial' /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']: --- /var/folders/yD/yDkhXjIsHAqkCTKsBbUlC++++TI/-Tmp-/expect31666.0 2009-01-04 11:23:35.000000000 +0000 +++ /var/folders/yD/yDkhXjIsHAqkCTKsBbUlC++++TI/-Tmp-/butwas31666.0 2009-01-04 11:23:35.000000000 +0000 @@ -1 +1 @@ -Paul P. Hughes +Paul P Hughes 2) Failure: test_full_name_without_middle_initial(UserTest) [./test/unit/user_test.rb:6:in `test_full_name_without_middle_initial' /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']: --- /var/folders/yD/yDkhXjIsHAqkCTKsBbUlC++++TI/-Tmp-/expect31666.1 2009-01-04 11:23:35.000000000 +0000 +++ /var/folders/yD/yDkhXjIsHAqkCTKsBbUlC++++TI/-Tmp-/butwas31666.1 2009-01-04 11:23:35.000000000 +0000 @@ -1 +1 @@ -John Smith +John Smith 2 tests, 2 assertions, 2 failures, 0 errors
Risultato del lancio automatico dei test con AutoTest che mostra i due test falliti.
Il primo test fallisce poichè non c’è un punto dopo l’iniziale del secondo nome, mentre il secondo fallisce perchè ci sono due spazi tra il nome e il cognome. Proveremo a sistemare prima il secondo problema. Ci sono due spazi perchè l’iniziale del secondo nome è nil
e stiamo unendo ogni elemento dell’array con uno spazio. Possiamo rimediare richamando il metodo compact
sull’array, prima di darlo in pasto alla join
. (compact
rimuove infatti tutti i valori nil
dall’array)
def full_name [first_name, middle_initial, last_name].compact.join(' ') end
Ora, solo il primo test fallisce: abbiamo bisogno di un punto subito dopo l’iniziale del secondo nome. Questo lo sistemiamo creando un nuovo metodo nella classe di modello User
chiamato middle_initial_with_full_stop
.
def full_name [first_name, middle_initial_with_full_stop, last_name].compact.join(' ') end def middle_initial_with_full_stop "#{middle_initial}." unless middle_initial.blank? end
I nostri test ora hanno successo, ma c’è una condizione che non abbiamo testato ancora. Cosa accade se nell’inizale del secondo nome c’è una stringa vuota? Possiamo scrivere un test per questo e vedere se ha successo.
test "full name with empty middle initial" do user = User.new(:first_name => "John", :middle_initial => "", :last_name => "Jones") assert_equal 'John Jones', user.full_name end
Anche questo passa. Il metodo blank?
per una stringa vuota, infatti, restituisce true
sia che si tratti di valore nil
, sia che si tratti di stringa vuota, per questo motivo funziona tutto.
Il nostro codice di modello User
ora sembra decisamente migliore di prima. C’è ancora della duplicazione nel codice di test, tuttavia. Ci guardaremo nel prossimo episodio.
Codice finale
class User < ActiveRecord::Base def full_name [first_name, middle_initial_with_full_stop, last_name].compact.join(' ') end def middle_initial_with_full_stop "#{middle_initial}." unless middle_initial.blank? end end
La classe di modello User
dopo il refactoring.
require 'test_helper' class UserTest < ActiveSupport::TestCase test "full name without middle initial" do user = User.new(:first_name => "John", :last_name => "Smith") assert_equal 'John Smith', user.full_name end test "full name with middle initial" do user = User.new(:first_name => "Paul", :middle_initial => "P", :last_name => "Hughes") assert_equal 'Paul P. Hughes', user.full_name end test "full name with empty middle initial" do user = User.new(:first_name => "John", :middle_initial => "", :last_name => "Jones") assert_equal 'John Jones', user.full_name end end
I test per la classe di modello User
.
Note
- Il RailsCast di questo episodio è basato su Rails 1. Il codice sopra è stato scritto usando Rails 2.2.
- http://rubyforge.org/projects/zentest/