SlideShare una empresa de Scribd logo
1 de 66
Integration testing
   with Cucumber:
How to test anything
        Dr Nic Williams
          mocra.com
       drnicwilliams.com
            @drnic

     $ sudo gem install tweettail
     $ tweettail jaoo -f
Ruby on Rails scenarios



Scenario: Login via OpenID
  Given I am on the home page
  And OpenID "http://drnicwilliams.com" maps to "Dr Nic"
  When I follow "login"
  And I fill-in "OpenID" with "http://drnicwilliams.com"
  And I press "Login"
  Then I should see "Welcome, Dr Nic"
Not just Rails
       newgem - package + deploy RubyGems
       tabtab - DSL for tab completions
       choctop - package + deploy Cocoa apps

Scenario: Build a DMG with default custom DMG config
  Given a Cocoa app with choctop installed called 'SampleApp'
  When task 'rake dmg' is invoked
  Then file 'appcast/build/SampleApp-0.1.0.dmg' is created
tweettail
$ sudo gem install tweettail
$ tweettail jaoo
mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
theRMK: Come speak with Matt at JAOO next week
drnic: reading my own abstract for JAOO presentation
New Gem in 2min
newgem tweet-tail
cd tweet-tail
script/generate executable tweettail
rake manifest
rake install_gem
Rakefile
$hoe = Hoe.new('tweettail', TweetTail::VERSION) do |p|
  p.developer('FIXME full name', 'FIXME email')
  ...
end




$hoe = Hoe.new('tweettail', TweetTail::VERSION) do |p|
  p.developer('Dr Nic', 'drnicwilliams@gmail.com')
  ...
end
README.rdoc
= tweet-tail

* FIX (url)




= tweet-tail

* http://github.com/drnic/tweet-tail
New Gem in 2min
          cont...
newgem tweet-tail
cd tweet-tail
script/generate executable tweettail
rake manifest
rake install_gem
tweettail
  SUCCESS!! To update this executable,
  look in lib/tweet-tail/cli.rb
User story
Feature: Live twitter search results
         on the command line

  In order to reduce cost of getting
         live search results
  As a twitter user

  I want twitter search results
         appearing in the console
Describe behaviour in plain text
Write a step definition in Ruby
Run and watch it fail
Fix code
Run and watch it pass!
Install cucumber

sudo gem install cucumber
script/generate install_cucumber
cp story_text features/command_line_app.feature
features/cli.feature
Feature: Live twitter search results on command line
  In order to reduce cost of getting live search results
  As a twitter user
  I want twitter search results appearing in the console

  Scenario: Display current search results
    Given twitter has some search results for "jaoo"
    When I run local executable "tweettail" with arguments "jaoo"
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week...
      Steve_Hayes: @VenessaP I think they went out for...
      theRMK: Come speak with Matt at JAOO next week...
      drnic: reading my own abstract for JAOO presentation...
      """
Running scenario
$ cucumber features/command_line_app.feature
...
1 scenario
2 skipped steps
1 undefined step

You can implement step definitions for missing steps
with these snippets:

Given /^twitter has some search results for "([^"]*)"$/ do |arg1|
  pending
end
features/step_definitions/
                twitter_data_steps.rb

Given /^twitter has some search results for "([^"]*)"$/ do |query|
  FakeWeb.register_uri(
    :get,
    "http://search.twitter.com/search.json?q=#{query}",
    :file => File.dirname(__FILE__) +
        "/../fixtures/search-#{query}.json")
end
features/step_definitions/
            twitter_data_steps.rb


mkdir -p features/fixtures

curl http://search.twitter.com/search.json?q=jaoo > 
  features/fixtures/search-jaoo.json
features/fixtures/search-jaoo.rb
{
    "results": [
    {
        "text": "reading my own abstract for JAOO presentation",
        "from_user": "drnic",
        "id": 1666627310
    },
    {
        "text": "Come speak with Matt at JAOO next week",
        "from_user": "theRMK",
        "id": 1666334207
    },
    {
        "text": "@VenessaP I think they went out for noodles. #jaoo",
        "from_user": "Steve_Hayes",
        "id": 1666166639
    },
    {
        "text": "Come speak with me at JAOO next week - http://jaoo.dk/",
        "from_user": "mattnhodges",
        "id": 1664823944,
    }],
    "refresh_url": "?since_id=1682666650&q=jaoo",
    "results_per_page": 15,
    "next_page": "?page=2&max_id=1682666650&q=jaoo"
features/common/env.rb


gem "fakeweb"
require "fakeweb"

Before do
  FakeWeb.allow_net_connect = false
end
Running scenario
$ cucumber features/command_line_app.feature
...
  Scenario: Display current search results
    Given a safe folder
    And twitter has some search results for "jaoo"
    When I run local executable "tweettail" with arguments "jaoo"
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week...
      Steve_Hayes: @VenessaP I think they went out for noodles...
      theRMK: Come speak with Matt at JAOO next week
      drnic: reading my own abstract for JAOO presentation
      """

1 scenario
1 failed step
3 passed steps
http://www.slideshare.net/bmabey/outsidein-development-with-cucumber
fetching JSON feed



def initial_json_data
  Net::HTTP.get(URI.parse("http://search.twitter.com/search.json?q=#{query}"))
end
fakeweb failure?!
  Scenario: Display current search results
    Given a safe folder
    And twitter has some search results for "jaoo"
    When I run local executable "tweettail" with arguments "jaoo"
getaddrinfo: nodename nor servname provided, or not known
(SocketError)
	 from .../net/http.rb:564:in `open'
	 ...
	 from .../tweet-tail/lib/tweet-tail/tweet_poller.rb:24:in
`initial_json_data'
	 from .../tweet-tail/lib/tweet-tail/tweet_poller.rb:9:in `refresh'
	 from .../tweet-tail/lib/tweet-tail/cli.rb:39:in `execute'
	 from .../tweet-tail/bin/tweet-tail:10
    Then I dump stdout
features/step_definitions/
                    common_steps.rb

When /^I run local executable "(.*)" with arguments "(.*)"/ do |exec, arguments|
  @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
  executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", exec))
  in_project_folder do
    system "ruby #{executable} #{arguments} > #{@stdout}"
  end
end
Can I ignore a
There’s probably always something
you can’t quite test
Minimise that layer of code
Test the rest

                bin     main lib
Can I ignore a
  There’s probably always something
  you can’t quite test
  Minimise that layer of code
  Test the rest

                  bin     main lib
1x sanity check
Can I ignore a
  There’s probably always something
  you can’t quite test
  Minimise that layer of code
  Test the rest

                  bin      main lib
1x sanity check
                   all other integration tests
                        on internal code
bin/tweettail

                                       Do I ne ed to test this?
#!/usr/bin/env ruby
#
# Created on 2009-5-1 by Dr Nic Williams
# Copyright (c) 2009. All rights reserved.

require 'rubygems'
require File.expand_path(File.dirname(__FILE__) + "/../lib/tweet-tail")
require "tweet-tail/cli"

TweetTail::CLI.execute(STDOUT, ARGV)
features/cli.feature
...
  Scenario: Display current search results
    Given twitter has some search results for 'jaoo'
    When I run local executable 'tweettail' with arguments 'jaoo'
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week...
      Steve_Hayes: @VenessaP I think they went out for...
      theRMK: Come speak with Matt at JAOO next week...
      drnic: reading my own abstract for JAOO presentation...
      """
features/cli.feature
...
  Scenario: Display some search results
    Given a safe folder
    And twitter has some search results for "jaoo"
    When I run local executable "tweettail" with arguments "jaoo"
    Then I should see some twitter messages

  Scenario: Display explicit search results
    Given a safe folder
    And twitter has some search results for "jaoo"
    When I run executable internally with arguments "jaoo"
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week
      Steve_Hayes: @VenessaP I think they went out for
      theRMK: Come speak with Matt at JAOO next week
      drnic: reading my own abstract for JAOO presentation
      """
end result
$ rake install_gem
$ tweettail jaoo
JAOO: Linda R.: I used to be a mathematician - I couldn't very well have started...
bengeorge: Global Financial Crisises are cool: jaoo tix down to 250 for 2 days.
kflund: First day of work at the JAOO Tutorials in Sydney - visiting the Opera House
wa7son: To my Copenhagen Ruby or Java colleagues: Get to meet Ola Bini at JAOO Geek Nights
ldaley: I am going to JAOO... awesome.
jessechilcott: @smallkathryn it's an IT conference. http://jaoo.com.au/sydney-2009/ .
scotartt: Looking forward to JAOO Brisbane next week - http://jaoo.com.au/brisbane-2009/
scotartt: JAOO Brisbane 2009 http://ff.im/-2B5ja
gwillis: @tweval I would give #jaoo a 10.0
rowanb: Bags almost packed for Sydney. Scrum User Group then JAOO. Driving there
mattnhodges: busy rest of week ahead. Spking @ Wiki Wed. Atlassian booth babe @ JAOO
conference Syd Thurs & Fri. Kiama 4 Jase's wedding all w'end #fb
pcalcado: searching twiter for #jaoo first impressions.
kornys: #jaoo has been excellent so far - though my tutorials have been full of
Steve_Hayes: RT @martinjandrews: women in rails - provide child care at #railsconf
CaioProiete: Wish I could be at #JAOO Australia...
‘I run executable internally’ step
                        defn
When /^I run executable internally with arguments "(.*)"/ do |arguments|
  require 'rubygems'
  require File.dirname(__FILE__) + "/../../lib/tweet-tail"
  require "tweet-tail/cli"

  @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
  in_project_folder do
    TweetTail::CLI.execute(@stdout_io = StringIO.new, arguments.split(" "))
    @stdout_io.rewind
    File.open(@stdout, "w") { |f| f << @stdout_io.read }
  end
end
Many provided steps
Given    /^a safe folder/ do
Given    /^this project is active project folder/ do
Given    /^env variable $([w_]+) set to "(.*)"/ do |env_var, value|
Given    /"(.*)" folder is deleted/ do |folder|

When    /^I   invoke "(.*)" generator with arguments "(.*)"$/ do |generator, args|
When    /^I   run executable "(.*)" with arguments "(.*)"/ do |executable, args|
When    /^I   run project executable "(.*)" with arguments "(.*)"/ do |executable, args|
When    /^I   run local executable "(.*)" with arguments "(.*)"/ do |executable, args|
When    /^I   invoke task "rake (.*)"/ do |task|

Then    /^folder "(.*)" (is|is not) created/ do |folder, is|
Then    /^file "(.*)" (is|is not) created/ do |file, is|
Then    /^file with name matching "(.*)" is created/ do |pattern|
Then    /^file "(.*)" contents (does|does not) match /(.*)// do |file, does, regex|
Then    /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
Then    /^I should see$/ do |text|
Then    /^I should not see$/ do |text|
Then    /^I should see exactly$/ do |text|
Then    /^I should see all (d+) tests pass/ do |expected_test_count|
Then    /^I should see all (d+) examples pass/ do |expected_test_count|
Then    /^Rakefile can display tasks successfully/ do
Then    /^task "rake (.*)" is executed successfully/ do |task|
‘I should see...’
 features/step_definitions/common_steps.rb
Then /^I should see$/ do |text|
  actual_output = File.read(@stdout)
  actual_output.should contain(text)
end

Then /^I should not see$/ do |text|
  actual_output = File.read(@stdout)
  actual_output.should_not contain(text)
end

Then /^I should see exactly$/ do |text|
  actual_output = File.read(@stdout)
  actual_output.should == text
end
‘When I do something...’


        features/step_definitions/common_steps.rb
When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, args|
  @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
  in_project_folder do
    system "ruby #{executable} #{arguments} > #{@stdout}"
  end
end

When /^I invoke task "rake (.*)"/ do |task|
  @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
  in_project_folder do
    system "rake #{task} --trace > #{@stdout}"
  end
end
‘Given a safe folder...’



                  features/support/env.rb
Before do
  @tmp_root = File.dirname(__FILE__) + "/../../tmp"
  @home_path = File.expand_path(File.join(@tmp_root, "home"))
  FileUtils.rm_rf   @tmp_root
  FileUtils.mkdir_p @home_path
  ENV["HOME"] = @home_path
end
tweettail jaoo -f


    polling please?


how to test polling?
features/cli.feature
Scenario: Poll for results until app cancelled
  Given twitter has some search results for "jaoo"
  When I run executable internally with arguments "jaoo -f"
  Then I should see
    """
    mattnhodges: Come speak with me at JAOO next week
    Steve_Hayes: @VenessaP I think they went out for
    theRMK: Come speak with Matt at JAOO next week
    drnic: reading my own abstract for JAOO presentation
    """
  When the sleep period has elapsed
  Then I should see
    """
    mattnhodges: Come speak with me at JAOO next week
    Steve_Hayes: @VenessaP I think they went out for
    theRMK: Come speak with Matt at JAOO next week
    drnic: reading my own abstract for JAOO presentation
    CaioProiete: Wish I could be at #JAOO Australia...
    """
  When I press "Ctrl-C"
  ...
adding -f option
$ cucumber features/cli.feature:22
...
  Scenario: Poll for results until app cancelled
    Given twitter has some search results for "jaoo"
    When I run executable internally with arguments "jaoo -f"
      invalid option: -f (OptionParser::InvalidOption)



                             lib/tweet-tail/cli.rb
module TweetTail::CLI
  def self.execute(stdout, arguments=[])
    options = { :polling => false }
    parser = OptionParser.new do |opts|
      opts.on("-f", "Poll for new search results each 15 seconds."
              ) { |arg| options[:polling] = true }
      opts.parse!(arguments)
    end

    app = TweetTail::TweetPoller.new(arguments.shift, options)
    app.refresh
    stdout.puts app.render_latest_results
  end
end
features/fixtures/
              search-jaoo-


{
    "results": [{
        "text": "Wish I could be at #JAOO Australia...",
        "from_user": "CaioProiete",
        "id": 1711269079,
    }],
    "since_id": 1682666650,
    "refresh_url": "?since_id=1711269079&q=jaoo",
    "query": "jaoo"
}
features/step_definitions/
                 twitter_data_steps.rb

Given /^twitter has some search results for "([^"]*)"$/ do |query|
  FakeWeb.register_uri(
    :get,
    "http://search.twitter.com/search.json?q=#{query}",
    :file => File.expand_path(File.dirname(__FILE__) +
             "/../fixtures/search-#{query}.json"))

  since = "1682666650"
  FakeWeb.register_uri(
    :get,
    "http://search.twitter.com/search.json?since_id=#{since}&q=#{query}",
    :file => File.expand_path(File.dirname(__FILE__) +
             "/../fixtures/search-#{query}-since-#{since}.json"))
end
hmm, sleep...
$ cucumber features/cli.feature:22
...
  Scenario: Poll for results until app cancelled
    Given twitter has some search results for "jaoo"
    When I run executable internally with arguments "jaoo -f"
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
      Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
      theRMK: Come speak with Matt at JAOO next week
      drnic: reading my own abstract for JAOO presentation
      """
    When the sleep period has elapsed
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
      Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
      theRMK: Come speak with Matt at JAOO next week
      drnic: reading my own abstract for JAOO presentation
      CaioProiete: Wish I could be at #JAOO Australia...
      """
features/cli.feature


  Scenario: Poll for results until app cancelled
    Given twitter has some search results for "jaoo"
    When I run executable internally with arguments "jaoo -f"
and wait 1 sleep cycle and quit
    Then I should see
      """
      mattnhodges: Come speak with me at JAOO next week
      Steve_Hayes: @VenessaP I think they went out for...
      theRMK: Come speak with Matt at JAOO next week
      drnic: reading my own abstract for JAOO presentation
      CaioProiete: Wish I could be at #JAOO Australia...
      """
features/step_definitions/executable_steps.rb
When /^I run executable internally with arguments "([^"]*)" and
wait (d+) sleep cycles? and quit$/ do |args, cycles|
  hijack_sleep(cycles.to_i)
  When %Q{I run executable internally with arguments "#{args}"}
end




         features/support/time_machine_helpers.rb
module TimeMachineHelper
  # expects sleep() to be called +cycles+ times, and then raises an Interrupt
  def hijack_sleep(cycles)
    results = [*1..cycles] # irrelevant truthy values for each sleep call
    Kernel::stubs(:sleep).returns(*results).then.raises(Interrupt)
  end
end
World(TimeMachineHelper)
using mocha
 require "mocha"

 World(Mocha::Standalone)

 Before do
   mocha_setup
 end

 After do
   begin
     mocha_verify
   ensure
     mocha_teardown
   end
 end

features/support/mocha.rb
working!
$ cucumber features/cli.feature:22
Feature: Live twitter search results on command line
  In order to reduce cost of getting live search results
  As a twitter user
  I want twitter search results appearing in the console

  Scenario: Poll for results until app cancelled
     Given twitter has some search results for "jaoo"
     When I run executable internally with arguments "jaoo -f" and wait 1 sleep cycle and
quit
     Then I should see
       """
       mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
       Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
       theRMK: Come speak with Matt at JAOO next week
       drnic: reading my own abstract for JAOO presentation
       CaioProiete: Wish I could be at #JAOO Australia...
       """

1 scenario (1 passed)
3 steps (3 passed)
Rakefile
              task :default => [:features]

$ rake
(in /Users/drnic/Documents/ruby/gems/tweet-tail)
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w -
Ilib:ext:bin:test -e 'require "rubygems"; require "test/unit"; require "test/
test_helper.rb"; require "test/test_tweet_poller.rb"'
Started
....
Finished in 0.002231 seconds.

4 tests, 10 assertions, 0 failures, 0 errors
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I "/Library/
Ruby/Gems/1.8/gems/cucumber-0.3.2/lib:lib" ...
.................

5 scenarios (5 passed)
17 steps (17 passed)


                         Run unit tests + features
1. Create account with
    http://runcoderun.com
2. Press ‘Test Hook’
1. Create account with
    http://runcoderun.com
2. Press ‘Test Hook’
Integration testing
   with Cucumber:
How to test anything
       Dr Nic Williams
         mocra.com
      drnicwilliams.com
       twitter: @drnic
      drnic@mocra.com

Más contenido relacionado

La actualidad más candente

How to Use Selenium, Successfully
How to Use Selenium, SuccessfullyHow to Use Selenium, Successfully
How to Use Selenium, SuccessfullySauce Labs
 
Test automation with Cucumber-JVM
Test automation with Cucumber-JVMTest automation with Cucumber-JVM
Test automation with Cucumber-JVMAlan Parkinson
 
APIs: A Better Alternative to Page Objects
APIs: A Better Alternative to Page ObjectsAPIs: A Better Alternative to Page Objects
APIs: A Better Alternative to Page ObjectsSauce Labs
 
Behavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using CucumberBehavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using CucumberKMS Technology
 
Creating a Plug-In Architecture
Creating a Plug-In ArchitectureCreating a Plug-In Architecture
Creating a Plug-In Architectureondrejbalas
 
Automated testing with Drupal
Automated testing with DrupalAutomated testing with Drupal
Automated testing with DrupalPromet Source
 
Laravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionLaravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionJoe Ferguson
 
Laravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionLaravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionJoe Ferguson
 
Session on Selenium Powertools by Unmesh Gundecha
Session on Selenium Powertools by Unmesh GundechaSession on Selenium Powertools by Unmesh Gundecha
Session on Selenium Powertools by Unmesh GundechaAgile Testing Alliance
 
Nightwatch at Tilt
Nightwatch at TiltNightwatch at Tilt
Nightwatch at TiltDave King
 
Testing with Codeception (Webelement #30)
Testing with Codeception (Webelement #30)Testing with Codeception (Webelement #30)
Testing with Codeception (Webelement #30)Adam Štipák
 
Front-End Testing: Demystified
Front-End Testing: DemystifiedFront-End Testing: Demystified
Front-End Testing: DemystifiedSeth McLaughlin
 
Join the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.jsJoin the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.jsSeth McLaughlin
 
Better End-to-End Testing with Page Objects Model using Protractor
Better End-to-End Testing with Page Objects Model using ProtractorBetter End-to-End Testing with Page Objects Model using Protractor
Better End-to-End Testing with Page Objects Model using ProtractorKasun Kodagoda
 
Nexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and SprayNexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and SprayMatthew Farwell
 
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with BehatGrand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with BehatRyan Weaver
 
Intro to JavaScript Tooling in Visual Studio Code
Intro to JavaScript Tooling in Visual Studio CodeIntro to JavaScript Tooling in Visual Studio Code
Intro to JavaScript Tooling in Visual Studio CodeColdFusionConference
 
MidwestPHP 2016 - Adventures in Laravel 5
MidwestPHP 2016 - Adventures in Laravel 5 MidwestPHP 2016 - Adventures in Laravel 5
MidwestPHP 2016 - Adventures in Laravel 5 Joe Ferguson
 
Selenium RC: Automated Testing of Modern Web Applications
Selenium RC: Automated Testing of Modern Web ApplicationsSelenium RC: Automated Testing of Modern Web Applications
Selenium RC: Automated Testing of Modern Web Applicationsqooxdoo
 
php[world] 2015 Laravel 5.1: From Homestead to the Cloud
php[world] 2015 Laravel 5.1: From Homestead to the Cloudphp[world] 2015 Laravel 5.1: From Homestead to the Cloud
php[world] 2015 Laravel 5.1: From Homestead to the CloudJoe Ferguson
 

La actualidad más candente (20)

How to Use Selenium, Successfully
How to Use Selenium, SuccessfullyHow to Use Selenium, Successfully
How to Use Selenium, Successfully
 
Test automation with Cucumber-JVM
Test automation with Cucumber-JVMTest automation with Cucumber-JVM
Test automation with Cucumber-JVM
 
APIs: A Better Alternative to Page Objects
APIs: A Better Alternative to Page ObjectsAPIs: A Better Alternative to Page Objects
APIs: A Better Alternative to Page Objects
 
Behavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using CucumberBehavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using Cucumber
 
Creating a Plug-In Architecture
Creating a Plug-In ArchitectureCreating a Plug-In Architecture
Creating a Plug-In Architecture
 
Automated testing with Drupal
Automated testing with DrupalAutomated testing with Drupal
Automated testing with Drupal
 
Laravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionLaravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello Production
 
Laravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello ProductionLaravel Forge: Hello World to Hello Production
Laravel Forge: Hello World to Hello Production
 
Session on Selenium Powertools by Unmesh Gundecha
Session on Selenium Powertools by Unmesh GundechaSession on Selenium Powertools by Unmesh Gundecha
Session on Selenium Powertools by Unmesh Gundecha
 
Nightwatch at Tilt
Nightwatch at TiltNightwatch at Tilt
Nightwatch at Tilt
 
Testing with Codeception (Webelement #30)
Testing with Codeception (Webelement #30)Testing with Codeception (Webelement #30)
Testing with Codeception (Webelement #30)
 
Front-End Testing: Demystified
Front-End Testing: DemystifiedFront-End Testing: Demystified
Front-End Testing: Demystified
 
Join the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.jsJoin the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.js
 
Better End-to-End Testing with Page Objects Model using Protractor
Better End-to-End Testing with Page Objects Model using ProtractorBetter End-to-End Testing with Page Objects Model using Protractor
Better End-to-End Testing with Page Objects Model using Protractor
 
Nexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and SprayNexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and Spray
 
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with BehatGrand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
 
Intro to JavaScript Tooling in Visual Studio Code
Intro to JavaScript Tooling in Visual Studio CodeIntro to JavaScript Tooling in Visual Studio Code
Intro to JavaScript Tooling in Visual Studio Code
 
MidwestPHP 2016 - Adventures in Laravel 5
MidwestPHP 2016 - Adventures in Laravel 5 MidwestPHP 2016 - Adventures in Laravel 5
MidwestPHP 2016 - Adventures in Laravel 5
 
Selenium RC: Automated Testing of Modern Web Applications
Selenium RC: Automated Testing of Modern Web ApplicationsSelenium RC: Automated Testing of Modern Web Applications
Selenium RC: Automated Testing of Modern Web Applications
 
php[world] 2015 Laravel 5.1: From Homestead to the Cloud
php[world] 2015 Laravel 5.1: From Homestead to the Cloudphp[world] 2015 Laravel 5.1: From Homestead to the Cloud
php[world] 2015 Laravel 5.1: From Homestead to the Cloud
 

Destacado (8)

Integration test
Integration testIntegration test
Integration test
 
Integration Test Engineer
Integration Test EngineerIntegration Test Engineer
Integration Test Engineer
 
Unit Testing Guidelines
Unit Testing GuidelinesUnit Testing Guidelines
Unit Testing Guidelines
 
Integration test
Integration testIntegration test
Integration test
 
Bse ppt[2]
Bse ppt[2]Bse ppt[2]
Bse ppt[2]
 
Unit and integration Testing
Unit and integration TestingUnit and integration Testing
Unit and integration Testing
 
Elements Of An Effective Bridge Management Plan
Elements Of An Effective Bridge Management PlanElements Of An Effective Bridge Management Plan
Elements Of An Effective Bridge Management Plan
 
BSE and NSE Stock Exchange
BSE and NSE Stock ExchangeBSE and NSE Stock Exchange
BSE and NSE Stock Exchange
 

Similar a Integration Testing With Cucumber How To Test Anything J A O O 2009

A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends旻琦 潘
 
Monkeybars in the Manor
Monkeybars in the ManorMonkeybars in the Manor
Monkeybars in the Manormartinbtt
 
Web Development with Sinatra
Web Development with SinatraWeb Development with Sinatra
Web Development with SinatraBob Nadler, Jr.
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
Automatisation in development and testing - within budget
Automatisation in development and testing - within budgetAutomatisation in development and testing - within budget
Automatisation in development and testing - within budgetDavid Lukac
 
"I have a framework idea" - Repeat less, share more.
"I have a framework idea" - Repeat less, share more."I have a framework idea" - Repeat less, share more.
"I have a framework idea" - Repeat less, share more.Fabio Milano
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapesJosé Paumard
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdevFrank Rousseau
 
Docker for data science
Docker for data scienceDocker for data science
Docker for data scienceCalvin Giles
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e bigAndy Peterson
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StoryKon Soulianidis
 
Building native Android applications with Mirah and Pindah
Building native Android applications with Mirah and PindahBuilding native Android applications with Mirah and Pindah
Building native Android applications with Mirah and PindahNick Plante
 
Torquebox OSCON Java 2011
Torquebox OSCON Java 2011Torquebox OSCON Java 2011
Torquebox OSCON Java 2011tobiascrawley
 
The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaKonrad Malawski
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developersSergio Bossa
 

Similar a Integration Testing With Cucumber How To Test Anything J A O O 2009 (20)

Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends
 
Monkeybars in the Manor
Monkeybars in the ManorMonkeybars in the Manor
Monkeybars in the Manor
 
Web Development with Sinatra
Web Development with SinatraWeb Development with Sinatra
Web Development with Sinatra
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Sprockets
SprocketsSprockets
Sprockets
 
Automatisation in development and testing - within budget
Automatisation in development and testing - within budgetAutomatisation in development and testing - within budget
Automatisation in development and testing - within budget
 
"I have a framework idea" - Repeat less, share more.
"I have a framework idea" - Repeat less, share more."I have a framework idea" - Repeat less, share more.
"I have a framework idea" - Repeat less, share more.
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapes
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
 
Docker for data science
Docker for data scienceDocker for data science
Docker for data science
 
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e big
 
Single Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle Story
 
Introduce Django
Introduce DjangoIntroduce Django
Introduce Django
 
Building native Android applications with Mirah and Pindah
Building native Android applications with Mirah and PindahBuilding native Android applications with Mirah and Pindah
Building native Android applications with Mirah and Pindah
 
Torquebox OSCON Java 2011
Torquebox OSCON Java 2011Torquebox OSCON Java 2011
Torquebox OSCON Java 2011
 
The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and Akka
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developers
 

Más de Dr Nic Williams

RailsConf Keynote - History of Ruby
RailsConf Keynote - History of RubyRailsConf Keynote - History of Ruby
RailsConf Keynote - History of RubyDr Nic Williams
 
What is your job at your ruby club?
What is your job at your ruby club?What is your job at your ruby club?
What is your job at your ruby club?Dr Nic Williams
 
Living With 1000 Open Source Projects
Living With 1000 Open Source ProjectsLiving With 1000 Open Source Projects
Living With 1000 Open Source ProjectsDr Nic Williams
 
Rubygem Dev And Workflow
Rubygem Dev And WorkflowRubygem Dev And Workflow
Rubygem Dev And WorkflowDr Nic Williams
 
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008Dr Nic Williams
 
Meta Meta Programming - EuRuKo2008
Meta Meta Programming -  EuRuKo2008Meta Meta Programming -  EuRuKo2008
Meta Meta Programming - EuRuKo2008Dr Nic Williams
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteDr Nic Williams
 
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxRubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxDr Nic Williams
 

Más de Dr Nic Williams (9)

RailsConf Keynote - History of Ruby
RailsConf Keynote - History of RubyRailsConf Keynote - History of Ruby
RailsConf Keynote - History of Ruby
 
What is your job at your ruby club?
What is your job at your ruby club?What is your job at your ruby club?
What is your job at your ruby club?
 
Living With 1000 Open Source Projects
Living With 1000 Open Source ProjectsLiving With 1000 Open Source Projects
Living With 1000 Open Source Projects
 
Rubygem Dev And Workflow
Rubygem Dev And WorkflowRubygem Dev And Workflow
Rubygem Dev And Workflow
 
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008
Everyone Can Participate - Dr Nic Williams - Railssummit Brazil 2008
 
Meta Meta Programming - EuRuKo2008
Meta Meta Programming -  EuRuKo2008Meta Meta Programming -  EuRuKo2008
Meta Meta Programming - EuRuKo2008
 
Why Use Rails by Dr Nic
Why Use Rails by  Dr NicWhy Use Rails by  Dr Nic
Why Use Rails by Dr Nic
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
 
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxRubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
 

Último

"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 

Último (20)

"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 

Integration Testing With Cucumber How To Test Anything J A O O 2009

  • 1. Integration testing with Cucumber: How to test anything Dr Nic Williams mocra.com drnicwilliams.com @drnic $ sudo gem install tweettail $ tweettail jaoo -f
  • 2.
  • 3.
  • 4. Ruby on Rails scenarios Scenario: Login via OpenID Given I am on the home page And OpenID "http://drnicwilliams.com" maps to "Dr Nic" When I follow "login" And I fill-in "OpenID" with "http://drnicwilliams.com" And I press "Login" Then I should see "Welcome, Dr Nic"
  • 5. Not just Rails newgem - package + deploy RubyGems tabtab - DSL for tab completions choctop - package + deploy Cocoa apps Scenario: Build a DMG with default custom DMG config Given a Cocoa app with choctop installed called 'SampleApp' When task 'rake dmg' is invoked Then file 'appcast/build/SampleApp-0.1.0.dmg' is created
  • 6.
  • 7. tweettail $ sudo gem install tweettail $ tweettail jaoo mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation
  • 8. New Gem in 2min newgem tweet-tail cd tweet-tail script/generate executable tweettail rake manifest rake install_gem
  • 9. Rakefile $hoe = Hoe.new('tweettail', TweetTail::VERSION) do |p| p.developer('FIXME full name', 'FIXME email') ... end $hoe = Hoe.new('tweettail', TweetTail::VERSION) do |p| p.developer('Dr Nic', 'drnicwilliams@gmail.com') ... end
  • 10. README.rdoc = tweet-tail * FIX (url) = tweet-tail * http://github.com/drnic/tweet-tail
  • 11. New Gem in 2min cont... newgem tweet-tail cd tweet-tail script/generate executable tweettail rake manifest rake install_gem tweettail SUCCESS!! To update this executable, look in lib/tweet-tail/cli.rb
  • 12. User story Feature: Live twitter search results on the command line In order to reduce cost of getting live search results As a twitter user I want twitter search results appearing in the console
  • 13. Describe behaviour in plain text Write a step definition in Ruby Run and watch it fail Fix code Run and watch it pass!
  • 14. Install cucumber sudo gem install cucumber script/generate install_cucumber cp story_text features/command_line_app.feature
  • 15. features/cli.feature Feature: Live twitter search results on command line In order to reduce cost of getting live search results As a twitter user I want twitter search results appearing in the console Scenario: Display current search results Given twitter has some search results for "jaoo" When I run local executable "tweettail" with arguments "jaoo" Then I should see """ mattnhodges: Come speak with me at JAOO next week... Steve_Hayes: @VenessaP I think they went out for... theRMK: Come speak with Matt at JAOO next week... drnic: reading my own abstract for JAOO presentation... """
  • 16. Running scenario $ cucumber features/command_line_app.feature ... 1 scenario 2 skipped steps 1 undefined step You can implement step definitions for missing steps with these snippets: Given /^twitter has some search results for "([^"]*)"$/ do |arg1| pending end
  • 17. features/step_definitions/ twitter_data_steps.rb Given /^twitter has some search results for "([^"]*)"$/ do |query| FakeWeb.register_uri( :get, "http://search.twitter.com/search.json?q=#{query}", :file => File.dirname(__FILE__) + "/../fixtures/search-#{query}.json") end
  • 18. features/step_definitions/ twitter_data_steps.rb mkdir -p features/fixtures curl http://search.twitter.com/search.json?q=jaoo > features/fixtures/search-jaoo.json
  • 19. features/fixtures/search-jaoo.rb { "results": [ { "text": "reading my own abstract for JAOO presentation", "from_user": "drnic", "id": 1666627310 }, { "text": "Come speak with Matt at JAOO next week", "from_user": "theRMK", "id": 1666334207 }, { "text": "@VenessaP I think they went out for noodles. #jaoo", "from_user": "Steve_Hayes", "id": 1666166639 }, { "text": "Come speak with me at JAOO next week - http://jaoo.dk/", "from_user": "mattnhodges", "id": 1664823944, }], "refresh_url": "?since_id=1682666650&q=jaoo", "results_per_page": 15, "next_page": "?page=2&max_id=1682666650&q=jaoo"
  • 20. features/common/env.rb gem "fakeweb" require "fakeweb" Before do FakeWeb.allow_net_connect = false end
  • 21. Running scenario $ cucumber features/command_line_app.feature ... Scenario: Display current search results Given a safe folder And twitter has some search results for "jaoo" When I run local executable "tweettail" with arguments "jaoo" Then I should see """ mattnhodges: Come speak with me at JAOO next week... Steve_Hayes: @VenessaP I think they went out for noodles... theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation """ 1 scenario 1 failed step 3 passed steps
  • 23. fetching JSON feed def initial_json_data Net::HTTP.get(URI.parse("http://search.twitter.com/search.json?q=#{query}")) end
  • 24. fakeweb failure?! Scenario: Display current search results Given a safe folder And twitter has some search results for "jaoo" When I run local executable "tweettail" with arguments "jaoo" getaddrinfo: nodename nor servname provided, or not known (SocketError) from .../net/http.rb:564:in `open' ... from .../tweet-tail/lib/tweet-tail/tweet_poller.rb:24:in `initial_json_data' from .../tweet-tail/lib/tweet-tail/tweet_poller.rb:9:in `refresh' from .../tweet-tail/lib/tweet-tail/cli.rb:39:in `execute' from .../tweet-tail/bin/tweet-tail:10 Then I dump stdout
  • 25. features/step_definitions/ common_steps.rb When /^I run local executable "(.*)" with arguments "(.*)"/ do |exec, arguments| @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", exec)) in_project_folder do system "ruby #{executable} #{arguments} > #{@stdout}" end end
  • 26. Can I ignore a There’s probably always something you can’t quite test Minimise that layer of code Test the rest bin main lib
  • 27. Can I ignore a There’s probably always something you can’t quite test Minimise that layer of code Test the rest bin main lib 1x sanity check
  • 28. Can I ignore a There’s probably always something you can’t quite test Minimise that layer of code Test the rest bin main lib 1x sanity check all other integration tests on internal code
  • 29. bin/tweettail Do I ne ed to test this? #!/usr/bin/env ruby # # Created on 2009-5-1 by Dr Nic Williams # Copyright (c) 2009. All rights reserved. require 'rubygems' require File.expand_path(File.dirname(__FILE__) + "/../lib/tweet-tail") require "tweet-tail/cli" TweetTail::CLI.execute(STDOUT, ARGV)
  • 30. features/cli.feature ... Scenario: Display current search results Given twitter has some search results for 'jaoo' When I run local executable 'tweettail' with arguments 'jaoo' Then I should see """ mattnhodges: Come speak with me at JAOO next week... Steve_Hayes: @VenessaP I think they went out for... theRMK: Come speak with Matt at JAOO next week... drnic: reading my own abstract for JAOO presentation... """
  • 31. features/cli.feature ... Scenario: Display some search results Given a safe folder And twitter has some search results for "jaoo" When I run local executable "tweettail" with arguments "jaoo" Then I should see some twitter messages Scenario: Display explicit search results Given a safe folder And twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo" Then I should see """ mattnhodges: Come speak with me at JAOO next week Steve_Hayes: @VenessaP I think they went out for theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation """
  • 32. end result $ rake install_gem $ tweettail jaoo JAOO: Linda R.: I used to be a mathematician - I couldn't very well have started... bengeorge: Global Financial Crisises are cool: jaoo tix down to 250 for 2 days. kflund: First day of work at the JAOO Tutorials in Sydney - visiting the Opera House wa7son: To my Copenhagen Ruby or Java colleagues: Get to meet Ola Bini at JAOO Geek Nights ldaley: I am going to JAOO... awesome. jessechilcott: @smallkathryn it's an IT conference. http://jaoo.com.au/sydney-2009/ . scotartt: Looking forward to JAOO Brisbane next week - http://jaoo.com.au/brisbane-2009/ scotartt: JAOO Brisbane 2009 http://ff.im/-2B5ja gwillis: @tweval I would give #jaoo a 10.0 rowanb: Bags almost packed for Sydney. Scrum User Group then JAOO. Driving there mattnhodges: busy rest of week ahead. Spking @ Wiki Wed. Atlassian booth babe @ JAOO conference Syd Thurs &amp; Fri. Kiama 4 Jase's wedding all w'end #fb pcalcado: searching twiter for #jaoo first impressions. kornys: #jaoo has been excellent so far - though my tutorials have been full of Steve_Hayes: RT @martinjandrews: women in rails - provide child care at #railsconf CaioProiete: Wish I could be at #JAOO Australia...
  • 33. ‘I run executable internally’ step defn When /^I run executable internally with arguments "(.*)"/ do |arguments| require 'rubygems' require File.dirname(__FILE__) + "/../../lib/tweet-tail" require "tweet-tail/cli" @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) in_project_folder do TweetTail::CLI.execute(@stdout_io = StringIO.new, arguments.split(" ")) @stdout_io.rewind File.open(@stdout, "w") { |f| f << @stdout_io.read } end end
  • 34. Many provided steps Given /^a safe folder/ do Given /^this project is active project folder/ do Given /^env variable $([w_]+) set to "(.*)"/ do |env_var, value| Given /"(.*)" folder is deleted/ do |folder| When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, args| When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, args| When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, args| When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, args| When /^I invoke task "rake (.*)"/ do |task| Then /^folder "(.*)" (is|is not) created/ do |folder, is| Then /^file "(.*)" (is|is not) created/ do |file, is| Then /^file with name matching "(.*)" is created/ do |pattern| Then /^file "(.*)" contents (does|does not) match /(.*)// do |file, does, regex| Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator| Then /^I should see$/ do |text| Then /^I should not see$/ do |text| Then /^I should see exactly$/ do |text| Then /^I should see all (d+) tests pass/ do |expected_test_count| Then /^I should see all (d+) examples pass/ do |expected_test_count| Then /^Rakefile can display tasks successfully/ do Then /^task "rake (.*)" is executed successfully/ do |task|
  • 35. ‘I should see...’ features/step_definitions/common_steps.rb Then /^I should see$/ do |text| actual_output = File.read(@stdout) actual_output.should contain(text) end Then /^I should not see$/ do |text| actual_output = File.read(@stdout) actual_output.should_not contain(text) end Then /^I should see exactly$/ do |text| actual_output = File.read(@stdout) actual_output.should == text end
  • 36. ‘When I do something...’ features/step_definitions/common_steps.rb When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, args| @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) in_project_folder do system "ruby #{executable} #{arguments} > #{@stdout}" end end When /^I invoke task "rake (.*)"/ do |task| @stdout = File.expand_path(File.join(@tmp_root, "tests.out")) in_project_folder do system "rake #{task} --trace > #{@stdout}" end end
  • 37. ‘Given a safe folder...’ features/support/env.rb Before do @tmp_root = File.dirname(__FILE__) + "/../../tmp" @home_path = File.expand_path(File.join(@tmp_root, "home")) FileUtils.rm_rf @tmp_root FileUtils.mkdir_p @home_path ENV["HOME"] = @home_path end
  • 38. tweettail jaoo -f polling please? how to test polling?
  • 39. features/cli.feature Scenario: Poll for results until app cancelled Given twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo -f" Then I should see """ mattnhodges: Come speak with me at JAOO next week Steve_Hayes: @VenessaP I think they went out for theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation """ When the sleep period has elapsed Then I should see """ mattnhodges: Come speak with me at JAOO next week Steve_Hayes: @VenessaP I think they went out for theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation CaioProiete: Wish I could be at #JAOO Australia... """ When I press "Ctrl-C" ...
  • 40. adding -f option $ cucumber features/cli.feature:22 ... Scenario: Poll for results until app cancelled Given twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo -f" invalid option: -f (OptionParser::InvalidOption) lib/tweet-tail/cli.rb module TweetTail::CLI def self.execute(stdout, arguments=[]) options = { :polling => false } parser = OptionParser.new do |opts| opts.on("-f", "Poll for new search results each 15 seconds." ) { |arg| options[:polling] = true } opts.parse!(arguments) end app = TweetTail::TweetPoller.new(arguments.shift, options) app.refresh stdout.puts app.render_latest_results end end
  • 41. features/fixtures/ search-jaoo- { "results": [{ "text": "Wish I could be at #JAOO Australia...", "from_user": "CaioProiete", "id": 1711269079, }], "since_id": 1682666650, "refresh_url": "?since_id=1711269079&q=jaoo", "query": "jaoo" }
  • 42. features/step_definitions/ twitter_data_steps.rb Given /^twitter has some search results for "([^"]*)"$/ do |query| FakeWeb.register_uri( :get, "http://search.twitter.com/search.json?q=#{query}", :file => File.expand_path(File.dirname(__FILE__) + "/../fixtures/search-#{query}.json")) since = "1682666650" FakeWeb.register_uri( :get, "http://search.twitter.com/search.json?since_id=#{since}&q=#{query}", :file => File.expand_path(File.dirname(__FILE__) + "/../fixtures/search-#{query}-since-#{since}.json")) end
  • 43. hmm, sleep... $ cucumber features/cli.feature:22 ... Scenario: Poll for results until app cancelled Given twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo -f" Then I should see """ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation """ When the sleep period has elapsed Then I should see """ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation CaioProiete: Wish I could be at #JAOO Australia... """
  • 44. features/cli.feature Scenario: Poll for results until app cancelled Given twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo -f" and wait 1 sleep cycle and quit Then I should see """ mattnhodges: Come speak with me at JAOO next week Steve_Hayes: @VenessaP I think they went out for... theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation CaioProiete: Wish I could be at #JAOO Australia... """
  • 45. features/step_definitions/executable_steps.rb When /^I run executable internally with arguments "([^"]*)" and wait (d+) sleep cycles? and quit$/ do |args, cycles| hijack_sleep(cycles.to_i) When %Q{I run executable internally with arguments "#{args}"} end features/support/time_machine_helpers.rb module TimeMachineHelper # expects sleep() to be called +cycles+ times, and then raises an Interrupt def hijack_sleep(cycles) results = [*1..cycles] # irrelevant truthy values for each sleep call Kernel::stubs(:sleep).returns(*results).then.raises(Interrupt) end end World(TimeMachineHelper)
  • 46. using mocha require "mocha" World(Mocha::Standalone) Before do mocha_setup end After do begin mocha_verify ensure mocha_teardown end end features/support/mocha.rb
  • 47. working! $ cucumber features/cli.feature:22 Feature: Live twitter search results on command line In order to reduce cost of getting live search results As a twitter user I want twitter search results appearing in the console Scenario: Poll for results until app cancelled Given twitter has some search results for "jaoo" When I run executable internally with arguments "jaoo -f" and wait 1 sleep cycle and quit Then I should see """ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo theRMK: Come speak with Matt at JAOO next week drnic: reading my own abstract for JAOO presentation CaioProiete: Wish I could be at #JAOO Australia... """ 1 scenario (1 passed) 3 steps (3 passed)
  • 48.
  • 49. Rakefile task :default => [:features] $ rake (in /Users/drnic/Documents/ruby/gems/tweet-tail) /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w - Ilib:ext:bin:test -e 'require "rubygems"; require "test/unit"; require "test/ test_helper.rb"; require "test/test_tweet_poller.rb"' Started .... Finished in 0.002231 seconds. 4 tests, 10 assertions, 0 failures, 0 errors /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I "/Library/ Ruby/Gems/1.8/gems/cucumber-0.3.2/lib:lib" ... ................. 5 scenarios (5 passed) 17 steps (17 passed) Run unit tests + features
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58. 1. Create account with http://runcoderun.com 2. Press ‘Test Hook’
  • 59. 1. Create account with http://runcoderun.com 2. Press ‘Test Hook’
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66. Integration testing with Cucumber: How to test anything Dr Nic Williams mocra.com drnicwilliams.com twitter: @drnic drnic@mocra.com

Notas del editor

  1. Abstract: You can write a small Ruby library in only a few lines. But then it grows, it expands and then it starts to break and become a maintenance nightmare. Since its open source you just stop working on it. Users complain that the project has been abandoned. Your project ends up causing more grief for everyone than if you&apos;d never written it at all. Instead, we will learn to write all Ruby libraries, RubyGems with tests. This session is NOT about &quot;how to do TDD&quot;. More importantly this session will teach you: * the one command you should run before starting any new Ruby project * the best way to write tests for command-line apps, Rake tasks and other difficult to test code * how to do Continuous Integration of your Ruby/Rails libraries with runcoderun.com Once you know how to write tests for all Ruby code, you&apos;ll want to do it for even the smallest little libraries and have the confidence to know your code always works.
  2. The tools and attitudes to acceptance testing and unit testing your applications and libraries have been evolving and improving in all languages and frameworks. In the Ruby space, Cucumber is the one tool has dominated all conversations about acceptance testing.
  3. Most Ruby developers write Rails applications, so the most common usage for Cucumber is describing user stories for a web application.
  4. But its entirely possible to write acceptance tests for libraries, command line applications and generators too.
  5. Since we&amp;#x2019;re at JAOO, we want to track conversations on twitter about JAOO. Since we&amp;#x2019;re nerds, let&amp;#x2019;s pull these into to the command line and print them out. Perhaps we&amp;#x2019;ll also add a &amp;#x201C;tail&amp;#x201D; feature to sit there watching for new tweets as they come along. A twitter terminal client. Very nerdy. Very JAOO.
  6. This would be our sample (truncated) output from the previous example page. It will then sit there pulling down search results and printing new search results when they appear. We&amp;#x2019;ll use Ctrl-C to cancel.
  7. &amp;#x2018;newgem&amp;#x2019; is the one command you should run before starting any new Ruby project or Rails plugin.
  8. And we have a working command line application!
  9. In agile development we describe a user story. What is it that the user wants from our software system? And what is the value to them for it.
  10. story_text is the content from the &amp;#x201C;Basic user story&amp;#x201D; slide
  11. If this search query is called then the contents of the fixtures fill will be returned.
  12. If this search query is called then the contents of the fixtures fill will be returned.
  13. Save a real copy of actual data from the target feed into your project.
  14. This wires fakeweb into cucumber and asks it to throw errors if we ever ask for remote content that hasn&amp;#x2019;t been setup via FakeWeb.request_uri
  15. So when we run our feature scenarios again we just get the error. Why? We haven&amp;#x2019;t written any code yet; but we&amp;#x2019;ve finished setting up our integration test.
  16. Ben Mabey took 137 slides to discuss when and why to start with Cucumber and then progress to unit tests of sections of your code.
  17. Somewhere in our solution code we call out to the json feed, parse the JSON, and print out the results.
  18. If I disable my internet and run my tests then I should definitely see fakeweb coming into effect. But it doesn&amp;#x2019;t. Bugger. This is because &amp;#x201C;When I run local executable...&amp;#x201D; invokes the executable in new Ruby process. It knows nothing about fakeweb.
  19. This is the default implementation of &amp;#x201C;run local executable&amp;#x201D;. We actually make a pure external system call to run the app, just like a user.
  20. If you do need to stub out something - a remote service, change the clock, speed things up, then its a lot easier to do it within the same Ruby process. So we&amp;#x2019;ll break up our integration test: 1 sanity check to test that the bin/tweettail executable is wired up correctly and pulls down some twitter data. The rest of our integration tests will invoke the library code directly within the same Ruby process.
  21. If you do need to stub out something - a remote service, change the clock, speed things up, then its a lot easier to do it within the same Ruby process. So we&amp;#x2019;ll break up our integration test: 1 sanity check to test that the bin/tweettail executable is wired up correctly and pulls down some twitter data. The rest of our integration tests will invoke the library code directly within the same Ruby process.
  22. If you do need to stub out something - a remote service, change the clock, speed things up, then its a lot easier to do it within the same Ruby process. So we&amp;#x2019;ll break up our integration test: 1 sanity check to test that the bin/tweettail executable is wired up correctly and pulls down some twitter data. The rest of our integration tests will invoke the library code directly within the same Ruby process.
  23. There is a very thin wrapper around the library code which was auto-generated. This helps you trust that it should &amp;#x201C;Just Work&amp;#x201D;. So let&amp;#x2019;s just test the final part instead to test the ultimate result
  24. Original version that we couldn&amp;#x2019;t fake out the remote data...
  25. New version where we can. The first scenario is a thin sanity check: does our app actually pull down the twitter search data feed and print out some message. We have no idea what it will print out, just the structure. The second and all subsequent scenarios will start testing our executable within the ruby runtime, so we can stub out the remote HTTP calls.
  26. Now we go and write some code, install the gem locally, and run it.
  27. New version where we can. The first scenario is a thin sanity check: does our app actually pull down the twitter search data feed and print out some message. We have no idea what it will print out, just the structure. The second and all subsequent scenarios will start testing our executable within the ruby runtime, so we can stub out the remote HTTP calls.
  28. If you&amp;#x2019;ve used cucumber with rails you&amp;#x2019;ll have seen the provided steps for webrat like &amp;#x201C;When I follow &amp;#x2018;link&amp;#x2019;&amp;#x201D; and &amp;#x201C;When I select &amp;#x2018;some drop down&amp;#x2019;&amp;#x201D;. If you use the cucumber in a RubyGem you get many provided step definitions too. The basic relationship between then is STDOUT and the file system. Do something which prints to STDOUT or modifies the file system, and then test the output or files.
  29. To explore how this is happening, let&amp;#x2019;s look at it in reverse. When we want to test the STDOUT, we need to be able to view and explore it. So we&amp;#x2019;re reading the STDOUT from previous steps from a file. The file name is stored in @stdout. The reason I store STDOUT into files is so that when a scenario fails, I can easily peruse each generated STDOUT file and look at what was output myself. If I just kept STDOUT in memory between steps then I&amp;#x2019;d lose that.
  30. It is all the When steps that run commands or rake tasks or generators, which in turn creates new files and prints things out to STDOUT. We run each of these commands from an external system command, just like the user would do, and then store the STDOUT to a file. Its file name is always stored in @stdout.
  31. If we&amp;#x2019;re saving STDOUT to files, if we&amp;#x2019;re testing generators or other applications or libraries that create new files, where is a safe place to do that? All scenarios get a &amp;#x201C;safe folder&amp;#x201D;. You get a tmp folder within your project source folder. You even get a fake HOME folder and the $HOME env variable wired to it, incase your application wants to manipulate dot files or other content within a user&amp;#x2019;s home folder.
  32. Let&amp;#x2019;s write a scenario first for the -f option The aim is to only very vaguely care about &amp;#x201C;how the hell am I going to implement &amp;#x2018;the sleep period has elapsed&amp;#x2019;
  33. Let&amp;#x2019;s drive development now. First, adding a -f option.
  34. We&amp;#x2019;ll need some more sample data from twitter. Here&amp;#x2019;s what the JSON might look like on a subsequent call to the API. The since_id value in the file name comes from the original sample JSON data - the refresh_url value.