Lunedì, 9 Gennaio 2012 Commenti TDD è meglio che fare Unit Testing a posteriori

Forse non ce n’era il bisogno, ma se vi dovesse servire una ulteriore prova inconfutabile che scrivere i test mentre stai scrivendo il codice sia meglio che testare il codice a latere, eccola.

Questo è il codice del controller che avevo scritto per gestire una autenticazione OAuth verso Twitter in un precedente progetto:

oauth_controller.rb
1 class OauthController < ApplicationController 2 3 def twitter_oauth 4 oauth_consumer = TwitterUser.oauth_consumer 5 request_token = oauth_consumer.get_request_token(:oauth_callback => team_twitter_callback_url(@team)) 
6 session['request_token'] = request_token.token 
7 session['request_secret'] = request_token.secret 
8 redirect_to request_token.authorize_url 
9 end 
10 
11 def twitter_oauth_callback 
12 oauth_consumer = TwitterUser.oauth_consumer 
13 request_token = OAuth::RequestToken.new(oauth_consumer, session['request_token'], session['request_secret'])
14 access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier]) 
15 
16 client = TwitterUser.client(access_token.token, access_token.secret) 
17 user = TwitterUser.initialize_with_twitter_data(client.verify_credentials) 
18 user.team = @team 
19 user.access_token = access_token.token 
20 user.access_secret = access_token.secret 
21 user.save 
22 
23 flash_message :notice, "A new Twitter account has been successfully added." 
24 redirect_to profile_path(@team) 
25 end 
26 
27 end

Codice che funziona, a prima vista. Ma un po’ troppo complicato per essere un controller, o no? E cosa succede se per caso qualcosa va come non dovrebbe? Il codice è pronto a gestire tutti i casi?

Questo è il medesimo codice, ma riscritto in TDD:

oauth_controller.rb
1 class OauthController < ApplicationController 
2 
3 def twitter_oauth 
4 request = TwitterProfile.oauth_authorization_request(twitter_callback_url) 
5 session['request_token'] = request.token 
6 session['request_secret'] = request.secret 
7 redirect_to request.authorize_url 
8 end 
9 
10 def twitter_oauth_callback 
11 profile = TwitterProfile.new_from_oauth_authorization_response(session['request_token'], session['request_secret'], params[:oauth_verifier]) 
12 profile.user = current_user 
13 if profile.save 
14 redirect_to profile_path(profile), notice: "A new Twitter account has been successfully added." 
15 else 
16 redirect_to new_profile_path, alert: "We were no able to add the Twitter account: #{formatted_record_errors(profile)}" 17 end 
18 rescue OAuth::Unauthorized => e 
19 redirect_to new_profile_path, alert: "We were no able to add the Twitter account (#{e.message})" 
20 end 
21 
22 end

La differenza si nota, o sbaglio? Controller più snello, più comprensibile, modulare, e questo nonostante sia più complesso, dato che gestisce anche possibili errori durante l’autenticazione che il primo codice ignorava con disprezzo.

La cosa divertente del TDD è che la forma che prende il codice non viene decisa a priori, ma emerge in modo naturale durante la scrittura del test.

L’unica regola da seguire è: “se il test è troppo complicato da scrivere, spezza e modularizza”. Il resto viene da se’.