Slideshare.net (beta)

 
Post: 
Myspace Hi5 Friendster Xanga LiveJournal Facebook Blogger Tagged Typepad Freewebs BlackPlanet gigya icons



All comments

Add a comment on Slide 1

If you have a SlideShare account, login to comment; else you can comment as a guest


Showing 1-50 of 0 (more)

Intro a RSpec, BDD, webapps User Acceptance Testing

From garnierjm, 2 months ago

496 views  |  0 comments  |  0 favorites  |  5 downloads  |  1 embed (Stats)
 
 
 

Groups/Events

Not added to any group/event

 
 

Privacy InfoNew!

This slideshow is Public

 
Embed in your blog
Embed (wordpress.com)
custom

Slideshow Statistics
Total Views: 496
on Slideshare: 466
from embeds: 30* * Views from embeds since 21 Aug, 07

Slideshow transcript

Slide 1: Introduction à RSpec Framework de BDD & Tests d'Acceptation Jean-Michel Garnier | 21croissants.com XP Day France | 6 mai 2008

Slide 2: Bonjour! Hi! Hola!  Mon blog: 21croissants.com  linqia  Mind map: http://www.21croissants.com/bdd 2

Slide 3: Mind map de la présentation 3

Slide 4: Origine du BDD

Slide 5: Origine du BDD

Slide 6: 1975: The Mythical Man-Month ● ¼ Spécificier ● 1/6 Coder ● Relire le code! ● ½ Tests !!!

Slide 7: 1994: 1er framework Tests Unitaires Class: SetTestCase superclass: TestCase instance variables: empty full SetTestCase>>setUp empty := Set new. SetTestCase>>testAdd empty add: 5. self should: [empty includes: 5] Kent Beck, article “Simple Smalltalk Testing”

Slide 8: 1998: Junit class TestMath extends TestCase { public void testAdd() { int num1 = 2; int num2 = 2; int expected = 4; int result = Math.add(num1, num2); assertEquals(expected, result); } }

Slide 9: 2002: TDD Ecrire les tests avant le code

Slide 10: 2003: TestDox (Junit) public class FooTest extends TestCase { public void testIsASingleton() {} public void testAReallyLongNameIsAGoodThing() {} } génère la doc de la classe Foo - is a singleton - a really long name is a good thing

Slide 11: 2005: BDD=TDD+should+behaviour class OldSchoolAgileDeveloperTest < TestCase def setup @ an_agile_developer = OldSchoolAgileDeveloper.new end def test_should_write_tests_before_code assert_true @an_agile_developer.is_writing_code_before_tests? end

Slide 12: BDD is cool describe AgileDeveloperBDD, "behaves like a coooool dude" do before :each do @an_agile_developer= AgileDeveloperBDD.new end it { @an_agile_developer.should be_writing_code_before_specs }

Slide 13: TDD is old school BDD is cool!

Slide 14: 2008: La Controverse de Valladolid Le BDD a-t-il une âme? “L'apparition du terme BDD m'a énervé dès l'origine. Il n'y a rien de nouveau par rapport au TDD, on a juste l'impression que des gens on voulu se rendre intéressant en critiquant le TDD (mal fait) et en utilisant un nouveau terme pour ce qui n'est autre que le TDD (bien fait).” D.W. (19/02/2008 8:09)

Slide 15: Le BDD en une phrase "I would say that TDD is a tool to help you solve the problem of designing and implementing behavior. xUnit works fine in that regard, but RSpec reduces the semantic distance between the developer and the problem domain." Pat Maddox (26/07/2007 00:47)

Slide 16: 2006 - Présent: javascript ● JSSpec ● RSpec ● Screw.Unit ● expectation ● bacon ● easyj ● phpSpec ● shoulda ● Instinct ● .net ● jtestr ● erlang ● ...

Slide 17: Livres pour 2008

Slide 18: RSpec Framework de BDD

Slide 19: RSpec

Slide 20: Anatomie d'une “spec” describe Spec do before :each do @spec = Spec.new end describe "après avoir été créée" do Comportements before :each do end it "devrait spécifier une seule classe" it "devrait avoir 0 comportements décrits" Exemples # ... end describe "Lors de l'execution:" do # ...

Slide 21: exécutable Pending: . Une spec, après avoir été créée devrait spécifier une seule classe (Not Yet Implemented) . Une spec, après avoir été créée devrait avoir 0 comportements décrits (Not Yet Implemented) Finished in 0.159 seconds 2 examples, 0 failures, 2 pending

Slide 22: Anatomie d'un “exemple” it "devrait spécifier une classe" do @spec.specified_class.should == Spec end # Old School syntaxe TDD: it "devrait spécifier une classe" do assert_equals(Spec, @spec.specified_class) end

Slide 23: Plus de diff entre spec & code it "devrait avoir 0 comportements décrits" do @spec.should have(0).behaviours end # Old School syntaxe TDD: it "devrait avoir 0 comportements décrits" do assert_equals(0, @spec.behaviours.size) end

Slide 24: On écrit en langage “naturel” (VO) “fluent interface” de Martin Fowler 'foo'.should == 'foo' ''.should be_empty 'foo with bar'.should include('with') 'http://21croissants.com'.should match(/http://.+/i) nil.should be_nil 100.should < 200 (200 - 100).should == 100 [1,2,3].should have(3).items [].should be_empty [1,2,3].should include(2)

Slide 25: Tester en isolation avec les “mocks” describe Person, "age" do before :each do @bob = Person.new(:birth_date => "23/08/1975") end it "should calculate age depending on birthday" do un_jour_sans_fin = Date.strptime('06/05/2008', '%d/%m/%Y') Time.should_receive(:now).and_return(un_jour_sans_fin) @bob.age.should == 32 end end

Slide 26: Exemple: EcoComparateur PARIS → MARSEILLE

Slide 27: EcoComparateur En tant qu'eco-citoyen Je veux connaître les emissions de CO2 d'un 4x4 pour un trajet A/R donné Afin de réduire mon empreinte carbone exemples scénario 1 scénario 1 bla bla bla bla blabla bla bla bla4x4 Paris → Marseille bla bla bla

Slide 28: er 1 exemple EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: should find 313kg of CO2

Slide 29: Identifier les “classes” EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: should find 313kg of CO2

Slide 30: Conversion a RSpec describe EcoComparateur do describe "when it calculates CO2 emissions for a Paris-Marseille return," do describe "travelling with a SUV:" do it "should find 313kg of CO2" ...

Slide 31: “pending” EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: - should find 313kg of CO2 (PENDING: Not Yet Implemented) Pending: EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: should find 313kg of CO2 (Not Yet Implemented) Finished in 0.053053 seconds 1 example, 0 failures, 1 pending

Slide 32: describe EcoComparateur do before(:each) do @eco_comparateur = EcoComparateur.new end describe "when it calculates CO2 emissions for a Paris-Marseille return," do describe "travelling with a SUV:" do before :each do @suv = mock("Car") @suv.should_receive(:kg_of_co2_per_km).and_return(20) end it "should find 313kg of CO2" do @eco_comparateur.calculate_co2_emissions_for_an(@suv). travelling("Paris", "Marseille"). should == 313 end ...

Slide 33: Implémentation du squelette class EcoComparateur def calculate_co2_emissions_for_an(vehicle) chain do travelling do |from, to| # TODO Implementation goes HERE! end end end

Slide 34: Failure EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: - should find 313kg of CO2 (FAILED - 1) 1) 'EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: should find 313kg of CO2' FAILED expected "find 313 kg of CO2" but got nil ./spec/eco_comparateur_spec.rb:23: Finished in 0.186483 seconds 1 example, 1 failure

Slide 35: On se “mock” des autres classes!  Comment calculer la distance entre Paris et Marseille?  On s'en mock!

Slide 36: describe EcoComparateur do before(:each) do @geocoding_mock = mock("GeocodingHelper") @eco_comparateur = EcoComparateur.new(@geocoding_mock) end describe "when it calculates CO2 emissions for a Paris-Marseille return," do before :each do @geocoding_mock.should_receive(:calculate_distance_in_km). with("Paris", "Marseille").and_return(783) end describe "travelling with a SUV:" do before :each do @suv = mock("Car") @suv.should_receive(:kg_of_co2_per_km).and_return(20) end it "should find 313kg of CO2" do @eco_comparateur.calculate_co2_emissions_for_an(@suv). travelling("Paris", "Marseille"). should find 313 } end it "should tell an inconvenient truth to the SUV owner ..."

Slide 37: Implémentation class EcoComparateur def initialize(geocoding_helper) @geocoding_helper = geocoding_helper end def calculate_co2_emissions_for_an(vehicle) chain do travelling do |from, to| distance = geocoding_helper. calculate_distance_in_km(from, to) 2 * distance * vehicle.kg_of_co2_per_km end end

Slide 38: “success” ! EcoComparateur when it calculates CO2 emissions for a 'Paris-Marseille return', travelling with a SUV: - should find 313kg of CO2 Finished in 0.061868 seconds 1 example, 0 failures

Slide 39: Avantages

Slide 40: Design ● Le code de qualité est facile à tester ● TDD = moins de code “mort” ● Utilisation de “mocks” pour définir l'API (Design incrémental) Paris On Rail 2007 – Copyright (c) Garnier Jean-Michel. Licence: Creative Commons.

Slide 41: Définition API

Slide 42: Spécifications

Slide 43: La doc des classes s'écrit toute seule... Documentation exécutable ↔ Documentation toujours à jour !

Slide 44: Tests d'acceptation utilisateur

Slide 45: Tests IHM web et d'acceptation utilisateur

Slide 46: selenium-grid by Mr. Philippe Hanrigou  “grid” de machines avec selenium  machines virtuels : mac mini +Parrallel  SUPER RAPIDE!  capture d'écrans (ALT + TAB)

Slide 47: GRID de machines (physiques/virtuelles) selenium -core (js) selenium-grid RSpec

Slide 48: Le format de “Story” de Dan North Login d'un utilisateur En tant qu'utilisateur Je veux que l'accès à l'application nécessite un login / mot de passe Afin de protéger mes données personnelles

Slide 49: Critères d'acceptation: scénarios Scenario 1: L'utilisateur se trompe de password Etant donné que je suis ds la page de '/member/login' Lorsque je tape 'jean-michel@21croissants.fr' dans le champ 'email' Lorsque je tape 'xpday2008' dans le champ 'password' Lorsque je clique sur le bouton 'signin-form-submit' Alors je devrais voir le texte 'Linqia Member Log-in' Alors je devrais voir le message d'erreur 'Invalid login or password'

Slide 50: Jean-Claude VanDamisation Scenario: 1. L'utilisateur se trompe de password Given que je suis dans la page de '/member/login' When je tape 'jean-michel@21croissants.fr' dans le champ 'email' When je tape 'xpday2008' dans le champ 'password' When je clique sur le bouton 'signin-form-submit' Then je devrais voir le texte 'Linqia Member Log-in' Then je devrais voir le message d'erreur 'Invalid login or password'

Slide 51: Binding texte / RSpec Given que je suis dans la la page de '/member/login' steps_for(:login) do Given "que je suis dans la la page de '$path'" do |path| $browser.open path end ....... end

Slide 52: Binding (suite) When "je tape '$value' dans le champ '$field_name'" do|value, field_name| $browser.type field_name, value end When "je clique sur le bouton '$link'" do |link| $browser.click_and_wait link end Then "je devrais voir le texte '$text'" do |text| text.should be_present end

Slide 53: Demo

Slide 54: Merci de votre attention! Questions - Réponses 54

Slide 55: Bonus

Slide 56: Bonus (s'il reste du rab!)  L'équipe de RSpec: David, Aslak et Dan  Autotest  Couverture  Heckle  Mingle

Slide 57: David Chelimsky

Slide 58: Aslak Hellesøy (NO)

Slide 59: Dan North  jbehave (2004)  rbehave  Intégration dans RSpec + Brian Takita Dave Astels Steve Baker Luke Redpath

Slide 60: Autotest ● Problème: les specs s'executent pendant 10 min... ● Solution: Autotest n'execute que les specs  nécessaires Notifications visuelles (plugins Growl, Notify,...) et  sonores http://ph7spot.com/articles/getting_started_with_autotest Paris On Rail 2007 – Copyright (c) Garnier Jean-Michel. Licence: Creative Commons.

Slide 61: Garantir la couverture avec rcov  sudo gem install rcov ●  rake spec:rcov ● http://eigenclass.org/hiki.rb?rcov Paris On Rail 2007 – Copyright (c) Garnier Jean-Michel. Licence: Creative Commons.

Slide 62: Couverture détaillée

Slide 63: Heckle

Slide 64: Mingle