추천중입니다.
닫기 블로그로 보내기


설정된 블로그가 없습니다.

블로그 설정하기

슬라이드를 블로그에 보내는 중입니다.
마가린 바르기bookmarkr.netmetagsWzd.com네이버에 북마크하기다음에 북마크하기HanRSS에 북마크하기이올린에 북마크하기Pumfit에 글 올리기News2.0에 투고하기del.icio.us에 북마크하기
TAG
URL Copy_btn
EMBED Copy_btn
작성자가 등록한 다른 큐
댓글을 작성하기 위해서는 먼저 로그인 하셔야 합니다.
현재 댓글의 수는 0 개 입니다.
Page 0: Page 1: Integration Testing in Ruby with RSpec’s Story Automation Framework David Chelimsky articulatedman.com Page 2: Behaviour-Driven Development Page 3: BDD • Dan North/Aslak Hellesøy 2004 • Improve communication about Test Driven Development • JBehave Page 4: BDD • Second generation “full stack” Agile methodology rooted in: • Extreme Programming • Acceptance Test Driven Planning • Test Driven Development Page 5: RSpec Page 6: RSpec • Behaviour Driven Development Framework • Story Framework • Acceptance Test Driven Planning • Example Framework • Test Driven Development Page 7: RSpec Origins • Inspired by a blog post by Dave Astels • Authored by Steven Baker • Summer ‘05 • Maintained by me • Since Summer ‘06 http://daveastels.com/2005/07/05/a-new-look-at-test-driven-development/ Page 8: BDD Process Page 9: Process • Inject features discovered through analysis • Feature Injection - Chris Matts • Extract stories from features • Focus on outputs • Break stories down into scenarios • Acceptance Criteria Page 10: Feature Injection Page 11: Popping the “Why?” Stack Page 12: Popping the “Why?” Stack • I want people to be able to register • Why? • I want to know how many people are registered • So I can measure progress towards registration goals • Why? • Why? Page 13: Popping the “Why?” Stack • This is really annoying • I know. Why do you want to measure progress towards registration goals? Page 14: Popping the “Why?” Stack • This is really annoying • I know. Why do you want to measure progress towards registration goals? • SO THAT I CAN MANAGE COST! Page 15: Popping the “Why?” Stack • If you keep asking “why?”, you’ll eventually land on one of: • When you do, the answer to the previous “why?” is often a feature waiting to be discovered. • Generate/protect revenue • Reduce/manage cost Page 16: Focus on Outputs • Business value lies in what you get out of the system, not what you put into it. • Reports • Messages Page 17: User Stories • High level analysis and planning tool • “Token for a conversation” Page 18: So what does this all have to do with Integration Testing? Page 19: Integration Testing • Goals • Make sure the component parts play nice together system • Document the expected behaviour of the Page 20: BDD Stories • The next step in the process • Collections of automated scenarios Page 21: Conference Organizer (Example Application) Page 22: Example Story Page 23: Example Story Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 24: Title Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 25: Narrative Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 26: Narrative Format “The Connextra Format” Page 27: As a Role Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 28: I want Action Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 29: So that Goal Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 30: Narrative Format • Completely arbitrary, but ... • Look for a format that • Identifies the goal • Identifies the user/persona Page 31: Scenario Format Page 32: Given | When | Then The other GWT Page 33: Given | When | Then • A simple way of saying: • Pre-conditions, Event, Post-conditions • Context, Action, Outcome • Build, Operate, Check • Uncle Bob Martin Page 34: Given | When | Then • Words that can be understood equally well by: • stakeholders • business analysts • developers • testers Page 35: Given Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 36: When Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 37: Then Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 38: Automation Page 39: Ruby Story "measure progress towards registration goals",%( As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals ), :type => RailsStory, :steps_for => :registrations do Scenario "one registration shows as 1%" do Given "a goal of 200 registrations" When "1 attendee registers" Then "the goal should be 1% achieved" end Scenario "one registration less than the goal shows as 99%" do Given "a goal of 200 registrations" When "199 attendees register" Then "the goal should be 99% achieved" end end Page 40: Plain Text with_steps_for :registrations do run "#{File.dirname(__FILE__)}/measure_progress.story", :type => RailsStory end Page 41: Step Definitions Page 42: Direct Model Access Page 43: Given Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 44: Given steps_for :registrations do Given "a goal of $goal registrations" do |goal| @conference = Conference.create!( :name => "BDD", :goal => goal ) end end Page 45: When Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 46: When steps_for :registrations do ... When /(\d+) attendee(s?) register(s?)/ do |count,_,_| (1..(count.to_i)).each do |n| Registration.create!( :name => "Name #{n}", :email => "email#{n}@site.com", :conference => @conference ) end end end Page 47: Then Story: measure progress towards registration goals As a conference organizer I want to see a report of registrations So that I can measure progress towards registration goals Scenario: one registration shows as 1% Given a goal of 200 registrations When 1 attendee registers Then the goal should be 1% achieved Scenario: one registration less than the goal shows as 99% Given a goal of 200 registrations When 199 attendees register Then the goal should be 99% achieved Page 48: Then steps_for :registrations do ... Then "the goal should be $percentage% achieved" do |percentage| @conference.percentage_of_goal.should == percentage.to_i end end Page 49: Direct Model Access • Pros • More flexible • Less brittle • Cons • Less thorough • No views/controllers Page 50: (Almost) Full Stack using Rails Integration Test and Webrat Page 51: Rails Integration Test • Simulate HTTP requests • Goes through routing (unlike Rails functional tests) • Simulate multiple sessions Page 52: RailsStory • Wraps Rails Integration Test • Access to everything you get from Rails • Access to everything you get from RSpec Page 53: Given steps_for :registrations_through_ui do Given "a goal of $goal registrations" do |goal| get "/conferences/new" response.should have_tag( "form[action=?]", conferences_path) do with_tag("input#conference_name") with_tag("input#conference_goal") end @conference_name = "BDD #{Time.new.to_i}" post "/conferences", :conference => { :name => @conference_name, :goal => goal } end end Page 54: Duplication steps_for :registrations_through_ui do Given "a goal of $goal registrations" do |goal| get "/conferences/new" response.should have_tag( "form[action=?]", conferences_path) do with_tag("input#conference_name") with_tag("input#conference_goal") end @conference_name = "BDD #{Time.new.to_i}" post "/conferences", :conference => { :name => @conference_name, :goal => goal } end end Page 55: Webrat • Ruby Gem written by Bryan Helmkamp • http://github.com/brynary/webrat • stores DOM in memory • manipulates DOM • builds POST from DOM • logically binding the form to the POST Page 56: Given (with Webrat) steps_for :registrations_through_ui do Given "a goal of $goal registrations" do |goal| @conference_name = "BDD #{Time.new.to_i}" visits "/conferences/new" fills_in "Name", :with => @conference_name fills_in "Goal", :with => goal clicks_button "Goal" end end Page 57: When steps_for :registrations_through_ui do ... When /(\d+) attendee(s?) register(s?)/ do |count,_,_| (1..(count.to_i)).each do |n| visits "/registrations/new" fills_in "Name", :with => "Name #{n}" fills_in "E-Mail", :with => "email#{n}@site.com" selects @conference_name clicks_button end end end Page 58: Then steps_for :registrations_through_ui do ... Then "the goal should be $percentage% achieved" do |percentage| @conference = Conference.find_by_name(@conference_name) visits "/conferences/#{@conference.id}" response.should have_text(/#{percentage}%/) end end Page 59: Full Stack (sans browser) • Pros • Full stack • High level of coverage • Confidence Page 60: Full Stack (sans browser) • Cons • Full Stack • Subject to changes from larger area • Requires known html elements/structure • Not too bad if you follow conventions • Webrat helps too Page 61: Full Stack using Selenium-RC Page 62: Given steps_for :registrations_through_browser do Given "a goal of $goal registrations" do |goal| $browser.open "http://localhost:3000/conferences/new" @conference_name = "BDD #{Time.new.to_i}" $browser.type "conference_name", @conference_name $browser.type "conference_goal", goal $browser.submit "new_conference" $browser.wait_for_page_to_load(5000) @conference_url = $browser.get_location end end Page 63: When steps_for :registrations_through_browser do ... When /(\d+) attendee(s?) register(s?)/ do |count,_,_| (1..(count.to_i)).each do |n| $browser.open "http://localhost:3000/registrations/new" $browser.type "css=#registration_name", "Name #{n}" $browser.type "css=#registration_email", "email#{n}@site.com" $browser.select "css=#registration_conference_id", @conference_name $browser.submit "css=#new_registration" end end end Page 64: Then steps_for :registrations_through_browser do ... Then "the goal should be $percentage% achieved" do |percentage| $browser.open @conference_url $browser.get_text("css=#percentage_of_goal"). should =~ /#{percentage}%/ end end Page 65: Full Stack (with browser) • Pros • Test javascript/ajax • Test some aspects of HTML too • You can watch it! • High impact for customers Page 66: Full Stack (with browser) • Cons • Brittle • Like in-memory, coupled to entire stack • Possibly even more subject to UI changes • S L OW Page 67: Full Stack (with browser) • Recommendation • Use in-memory first • Use in-browser when • Testing javascript/ajax • Useful when necessary to increase customer confidence Page 68: Detailed Scenarios Page 69: Detailed Scenarios Story: attendee registers As a potential attendee I want to register for a conference So that I may attend and learn great stuff Scenario: successful registration Given I am viewing the registration form When I enter Name: Joe Smith And I enter E-Mail: jsmith@site.com And I check Tutorials And I click Register Then I should see the Registration Confirmation And it should show Name: Joe Smith And it should show E-Mail: jsmith@site.com And it should show Tutorials: Yes Page 70: Detailed Scenarios ... Scenario: missing email address Given I am viewing the registration form When I enter Name: Joe Smith And I do not enter E-Mail And I click Register Then I should see the Registration Form And it should show Email is required Page 71: Givens steps_for :registration do Given "I am viewing the registration form" do visits new_registration_path end Given "a conference named $name" do |name| visits new_conference_path fills_in "Name", :with => name fills_in "Goal", :with => 200 clicks_button end end Page 72: Whens steps_for :registration do When "I enter $label: $value" do |label, value| fills_in label, :with => value end When "I do not enter $label" do |label| # no-op - doc purposes only end end Page 73: Whens steps_for :registration do When "I select $label" do |label| selects label end When "I check $label" do |label| checks label end When "I click $button" do |button| clicks_button button end end Page 74: Thens steps_for :registration do Then /I should see the Registration (Form|Confirmation)/ do |form_or_confirmation| case form_or_confirmation when "Form" response.should render_template("registrations/new") when "Confirmation" r = Registration.find(:all, :order => 'id').last response.should render_template("registrations/show") end end Then "it should show $text" do |text| response.should include_text(text) end end Page 75: Detailed Scenarios • More “design up front” feel vs “story as token for conversation” customer • Scenarios are more subject to changes from • Steps are more granular • Easier to write • Easier to change Page 76: Multiple Sessions Page 77: Multiple Sessions Story "can not view other group's calendar", %( As a member of one group with calendars I do not want members of other groups to see my calendars So that my data is kept private ), :type => RailsStory, :steps_for => [ :users_and_groups, :calendars, :navigation ] do ... end Page 78: Multiple Sessions Story "can not view other group's calendar", %( ... Scenario "two users, two groups, two calendars" do Given "a group named Group1" And "a group named Group2" And "Group1 has a calendar named Calendar1" And "Group2 has a calendar named Calendar2" And "a Group1 member named Person1 is logged in" And "a Group2 member named Person2 is logged in" When "Person1 visits the calendar list" Then "he should see Calendar1" And "he should not see Calendar2" When "Person2 visits the calendar list" Then "she should see Calendar2" And "she should not see Calendar1" end end Page 79: Given Given "a group named $group" do |group| set_ivar :group, group, Group.create!(:name => group) end Given "a $group member named $name is logged in" do |group, name| create_user_named(name) do |user| user.activate user.groups << get_ivar(:group, group) end login_as(name) end Page 80: When When "$person visits the $page" do |person, page| page_map = { "calendar list" => "/calendars" } @current_session = get_ivar(:session, person) @current_session.visits page_map[page] end Page 81: Then Then /(he|she) (should|should not) see (.*)/ do |_, yes_or_no, calendar| if yes_or_no == 'should' @current_session.response.should include_text(calendar) else @current_session.response.should_not include_text(calendar) end end Page 82: Multiple Sessions def create_user_named(login, password=login) User.find_by_login(login).destroy rescue nil user = User.create!( :login => login, :password => password, :password_confirmation => password, :email => "#{login}@company.com" ) yield user if block_given? user end def login_as(login, password=login) set_ivar :session, login, open_session { |user| user.visits "/sessions/new" user.fills_in :login, :with => login user.fills_in :password, :with => password user.clicks_button } end Page 83: Multiple Sessions module InstanceVariableHelpers def set_ivar(type, name, obj) instance_variable_set ivar_name(type, name), obj end def get_ivar(type, name) returning instance_variable_get(ivar_name(type, name)) do |obj| yield obj if block_given? end end private def ivar_name(type, name) "@#{type}_#{name.gsub(/[ -]/,'_').gsub('&','and')}" end end Page 84: Thank You • rspec.info • blog.davidchelimsky.net • articulatedman.com Page 85: