SlideShare a Scribd company logo
1 of 43
TDD of HTTP Clients
  with WebMock
     Bartosz Blimke
     @bartoszblimke




                      new bamboo
Test



TDD
RED
RED


       Code


              new bamboo
Hello http://my.geoservice.org
    Give me location for zip code WC1H 9EF
GET http://my.geoservice.org/?zip=WC1H 9EF


                                 HTTP 200 - OK
                      Content-Type: application/json
                               Here is your location:
                     {"latitude"=>"51.52","longitude":-0.12"}




                                                           new bamboo
describe GeoCoordinatesFinder do

  it "should make http request to geocoding service" do



  end

  it "should report coordinates provided by geocoding service" do



  end

end




                                                          new bamboo
describe GeoCoordinatesFinder do

  it "should make http request to geocoding service" do

        ???
  end

  it "should report coordinates provided by geocoding service" do

        ???
  end

end




                                                          new bamboo
new bamboo
new bamboo
class GeoCoordinatesFinder

  def get_coordinates_by_zip_code(zip)
    res = Net::HTTP.start("my.geoservice.org", 80) { |http|
      http.get("/?zip=#{URI.encode(zip)}")
    }
    result = JSON.parse(res)
    [result['latitude'], result['longitude']]
  end

end




                                                      new bamboo
describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    @mock_http = mock("http")
    @res = mock(Net::HTTPOK, :body => "{"latitude":"5.5","longitude":"6.6"}")
    Net::HTTP.stub!(:start).with("my.geoservice.org", 80).and_yield @mock_http
    @mock_http.stub!(:get).with("/?zip=WC1H%209EF").and_return(@res)
  end

  it "should make http request to geocoding service" do
    Net::HTTP.should_receive(:start).and_yield @mock_http
    @mock_http.should_receive(:get).with("/?zip=WC1H%209EF")
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").should == ["5.5", "6.6"]
  end

end




                                                                            new bamboo
new bamboo
class GeoCoordinatesFinder

  def get_coordinates_by_zip_code(zip)
    url = "http://my.geoservice.org?zip=#{URI.encode(zip)}"
    response = RestClient.get(url)
    result = JSON.parse(response)
    [result['latitude'], result['longitude']]
  end

end




                                                     new bamboo
new bamboo
describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    RestClient.stub!(:get).with("http://my.geoservice.org?zip=WC1H%209EF").
      and_return("{"latitude":"5.5","longitude":"6.6"}")
  end

  it "should make http request to yahoo geocoding service" do
    RestClient.should_receive(:get).with("http://my.geoservice.org?zip=WC1H%209EF")
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").should == ["5.5", "6.6"]
  end

end




                                                                              new bamboo
I wanted a tool for stubbing
   HTTP requests that...
I wanted a tool for stubbing
    HTTP requests that...
• doesn’t depend on usage of a specific API
I wanted a tool for stubbing
    HTTP requests that...
• doesn’t depend on usage of a specific API
• supports stubbing based on request
  method, uri, body and headers
I wanted a tool for stubbing
    HTTP requests that...
• doesn’t depend on usage of a specific API
• supports stubbing based on request
  method, uri, body and headers
• verifies expectations of requests
I wanted a tool for stubbing
    HTTP requests that...
• doesn’t depend on usage of a specific API
• supports stubbing based on request
  method, uri, body and headers
• verifies expectations of requests
• supports different HTTP libraries
WebMock


          new bamboo
require 'webmock/rspec'
include WebMock

describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  it "should make http request to geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF")
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").
      should == ["5.5", "6.6"]
  end

end




                                                                     new bamboo
require 'webmock/rspec'
include WebMock

describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  it "should make http request to geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF")
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").
      should == ["5.5", "6.6"]
  end

end




                                                                     new bamboo
require 'webmock/rspec'
include WebMock

describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  it "should make http request to geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF")
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").
      should == ["5.5", "6.6"]
  end

end




                                                                     new bamboo
require 'webmock/rspec'
include WebMock

describe GeoCoordinatesFinder do

  before(:each) do
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  it "should make http request to geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    request(:get, "http://my.geoservice.org?zip=WC1H 9EF").should have_been_made
  end

  it "should report coordinates provided by geocoding service" do
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").
      should == ["5.5", "6.6"]
  end

end




                                                                     new bamboo
new bamboo
class GeoCoordinatesFinder

  def get_coordinates_by_zip_code(zip)
    uri = "http://my_geoservice.org?zip=#{URI.encode(zip)}"
    result = JSON.parse(HTTPClient.new.get(uri).content)
    [result['latitude'], result['longitude']]
  end

end




                                                     new bamboo
new bamboo
Test::Unit
require 'webmock/test_unit'
include WebMock

class TestGeoCoordinatesFinder < Test::Unit::TestCase

  def setup
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  def test_making_request_to_geocoding_service
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    assert_requested :get, "http://my.geoservice.org?zip=WC1H 9EF"
  end

  def test_reporting_coordinates_provided_by_geocoding_service
    assert_equal @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"), ["5.5", "6.6"]
  end

end




                                                                                new bamboo
Test::Unit
require 'webmock/test_unit'
include WebMock

class TestGeoCoordinatesFinder < Test::Unit::TestCase

  def setup
    @coordinates_finder = GeoCoordinatesFinder.new
    stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF").
      to_return(:body => "{"latitude":"5.5","longitude":"6.6"}")
  end

  def test_making_request_to_geocoding_service
    @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF")
    assert_requested :get, "http://my.geoservice.org?zip=WC1H 9EF"
  end

  def test_reporting_coordinates_provided_by_geocoding_service
    assert_equal @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"), ["5.5", "6.6"]
  end

end




                                                                                new bamboo
Cucumber
features/common/env.rb

require 'webmock'
include WebMock

Before do
  WebMock.disable_net_connect!
  WebMock.reset_webmock
end




                                 new bamboo
Cucumber
features/finding_geo_coordinates.feature
Feature: Finding the way to an awesome conference
  In order to find the best way to Ruby Manor
  As a ruby geek
  I want to see how to get there on a map

 Scenario: User finds zip code coordinates on a map
   Given geoservice reports coordinates "51.52, -0.12" for zip code "WC1H 9EF"
   When I type "WC1H 9EF" into zip code field
   And I press "Show on map"
   Then I should see map centered at latitude "51.52" and longitude "-0.12"




                                                                      new bamboo
Cucumber
  features/step_definitions/geocoding_service_steps.rb

Given /^geoservice reports (.+), (.+) as coordinates for zip code (.+)$/ do
|lat, long, zip_code|
  stub_request(:get, "http://my.geoservice.org?zip=#{zip_code}").
    to_return(
      :body => "{"latitude":"#{lat}","longitude":"#{long}"}"
    )
end




                                                                 new bamboo
Matching Requests
              on Body and Headers
stub_request(:post, "www.example.net").
  with(:body => "abc", :headers => { :content_type => 'text/plain' })

RestClient.post "www.example.net", "abc", :content_type => 'text/plain'

WebMock.should have_requested(:post, "www.example.net").
  with(:body => "abc", :headers => { 'Content-Type' => 'text/plain' })




                                                             new bamboo
Smart URI Matching

www.example.net/my path?x=my params




                                      new bamboo
Smart URI Matching

www.example.net/my path?x=my params
www.example.net/my%20path?x=my%20params
www.example.net:80/my path?x=my params
www.example.net:80/my%20path?x=my%20params
http://www.example.net/my path?x=my params
http://www.example.net/my%20path?x=my%20params
http://www.example.net:80/my path?x=my params
http://www.example.net:80/my%20path?x=my%20params




                                          new bamboo
Matching URIs with Regexp
stub_request(:post, /example/).
  with(:body => "abc", :headers => { :content_type => 'text/plain' })

RestClient.post "www.example.net", "abc", :content_type => 'text/plain'

WebMock.should have_requested(:post, /example/).
  with(:body => "abc", :headers => { 'Content-Type' => 'text/plain' })




                                                              new bamboo
Basic Authentication
stub_request(:any, 'user:pass@www.example.net').
  to_return(:body => 'abc')

RestClient::Resource.new(
  'www.example.net',
  :user => 'user',
  :password => 'pass'
).post('abc')   # ===> "abcn"

WebMock.should have_requested(:post, 'user:pass@www.example.net')




                                                        new bamboo
Dynamic Responses
stub_request(:any, 'www.example.net').
  to_return(:body => lambda { |request| request.body })

RestClient.post('www.example.net', 'abc')   # ===> "abcn"




                                                  new bamboo
Supported Libraries

Net::HTTP    HTTPClient




                          new bamboo
Supported Libraries

Net::HTTP         HTTPClient
HTTParty
RESTClient
RightScale::HttpConnection
open-uri



                               new bamboo
http://github.com/bblimke/webmock




                                    new bamboo
http://github.com/bblimke/webmock
http://groups.google.com/group/webmock-users




                                         new bamboo
http://github.com/bblimke/webmock
http://groups.google.com/group/webmock-users
bartosz@new-bamboo.co.uk
@bartoszblimke



                                         new bamboo
Questions?
http://github.com/bblimke/webmock
http://groups.google.com/group/webmock-users
bartosz@new-bamboo.co.uk
@bartoszblimke



                                         new bamboo

More Related Content

What's hot

Beyond Page Level Metrics
Beyond Page Level MetricsBeyond Page Level Metrics
Beyond Page Level MetricsPhilip Tellis
 
Go Concurrency
Go ConcurrencyGo Concurrency
Go ConcurrencyCloudflare
 
Ruby HTTP clients comparison
Ruby HTTP clients comparisonRuby HTTP clients comparison
Ruby HTTP clients comparisonHiroshi Nakamura
 
Implementing Comet using PHP
Implementing Comet using PHPImplementing Comet using PHP
Implementing Comet using PHPKing Foo
 
Http capturing
Http capturingHttp capturing
Http capturingEric Ahn
 
Puppet Performance Profiling
Puppet Performance ProfilingPuppet Performance Profiling
Puppet Performance Profilingripienaar
 
Beyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisBeyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisFastly
 
Flask With Server-Sent Event
Flask With Server-Sent EventFlask With Server-Sent Event
Flask With Server-Sent EventTencent
 
Testing your infrastructure with litmus
Testing your infrastructure with litmusTesting your infrastructure with litmus
Testing your infrastructure with litmusBram Vogelaar
 
HTTPBuilder NG: Back From The Dead
HTTPBuilder NG: Back From The DeadHTTPBuilder NG: Back From The Dead
HTTPBuilder NG: Back From The Deadnoamt
 
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!MongoDB World 2019: Becoming an Ops Manager Backup Superhero!
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!MongoDB
 
Observability with Consul Connect
Observability with Consul ConnectObservability with Consul Connect
Observability with Consul ConnectBram Vogelaar
 
Autoscaling with hashi_corp_nomad
Autoscaling with hashi_corp_nomadAutoscaling with hashi_corp_nomad
Autoscaling with hashi_corp_nomadBram Vogelaar
 
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...Willian Molinari
 
How we use and deploy Varnish at Opera
How we use and deploy Varnish at OperaHow we use and deploy Varnish at Opera
How we use and deploy Varnish at OperaCosimo Streppone
 
VUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareVUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareCosimo Streppone
 
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
Using Node.js to  Build Great  Streaming Services - HTML5 Dev ConfUsing Node.js to  Build Great  Streaming Services - HTML5 Dev Conf
Using Node.js to Build Great Streaming Services - HTML5 Dev ConfTom Croucher
 
Integrating icinga2 and the HashiCorp suite
Integrating icinga2 and the HashiCorp suiteIntegrating icinga2 and the HashiCorp suite
Integrating icinga2 and the HashiCorp suiteBram Vogelaar
 

What's hot (20)

Beyond Page Level Metrics
Beyond Page Level MetricsBeyond Page Level Metrics
Beyond Page Level Metrics
 
Go Concurrency
Go ConcurrencyGo Concurrency
Go Concurrency
 
Ruby HTTP clients comparison
Ruby HTTP clients comparisonRuby HTTP clients comparison
Ruby HTTP clients comparison
 
Implementing Comet using PHP
Implementing Comet using PHPImplementing Comet using PHP
Implementing Comet using PHP
 
Http capturing
Http capturingHttp capturing
Http capturing
 
Puppet Performance Profiling
Puppet Performance ProfilingPuppet Performance Profiling
Puppet Performance Profiling
 
Beyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisBeyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic Analysis
 
Flask With Server-Sent Event
Flask With Server-Sent EventFlask With Server-Sent Event
Flask With Server-Sent Event
 
Testing your infrastructure with litmus
Testing your infrastructure with litmusTesting your infrastructure with litmus
Testing your infrastructure with litmus
 
HTTPBuilder NG: Back From The Dead
HTTPBuilder NG: Back From The DeadHTTPBuilder NG: Back From The Dead
HTTPBuilder NG: Back From The Dead
 
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!MongoDB World 2019: Becoming an Ops Manager Backup Superhero!
MongoDB World 2019: Becoming an Ops Manager Backup Superhero!
 
Observability with Consul Connect
Observability with Consul ConnectObservability with Consul Connect
Observability with Consul Connect
 
Autoscaling with hashi_corp_nomad
Autoscaling with hashi_corp_nomadAutoscaling with hashi_corp_nomad
Autoscaling with hashi_corp_nomad
 
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...
How we used ruby to build locaweb's cloud (http://presentations.pothix.com/ru...
 
How we use and deploy Varnish at Opera
How we use and deploy Varnish at OperaHow we use and deploy Varnish at Opera
How we use and deploy Varnish at Opera
 
vBACD - Introduction to Opscode Chef - 2/29
vBACD - Introduction to Opscode Chef - 2/29vBACD - Introduction to Opscode Chef - 2/29
vBACD - Introduction to Opscode Chef - 2/29
 
VUG5: Varnish at Opera Software
VUG5: Varnish at Opera SoftwareVUG5: Varnish at Opera Software
VUG5: Varnish at Opera Software
 
Nodejs meetup-12-2-2015
Nodejs meetup-12-2-2015Nodejs meetup-12-2-2015
Nodejs meetup-12-2-2015
 
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
Using Node.js to  Build Great  Streaming Services - HTML5 Dev ConfUsing Node.js to  Build Great  Streaming Services - HTML5 Dev Conf
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
 
Integrating icinga2 and the HashiCorp suite
Integrating icinga2 and the HashiCorp suiteIntegrating icinga2 and the HashiCorp suite
Integrating icinga2 and the HashiCorp suite
 

Viewers also liked (20)

Green2
Green2Green2
Green2
 
Week4 a2
Week4 a2Week4 a2
Week4 a2
 
S P W13
S P W13S P W13
S P W13
 
B
BB
B
 
Energy Efficiency In The Office
Energy Efficiency In The OfficeEnergy Efficiency In The Office
Energy Efficiency In The Office
 
Week10 b
Week10 bWeek10 b
Week10 b
 
Sp W13
Sp W13Sp W13
Sp W13
 
Espolon Oeste
Espolon OesteEspolon Oeste
Espolon Oeste
 
Espolon Oeste
Espolon OesteEspolon Oeste
Espolon Oeste
 
Soil
SoilSoil
Soil
 
C w6
C w6C w6
C w6
 
C w3
C w3C w3
C w3
 
Week4 a2
Week4 a2Week4 a2
Week4 a2
 
B w6
B w6B w6
B w6
 
Week9 b
Week9 bWeek9 b
Week9 b
 
C w3
C w3C w3
C w3
 
Sp W12
Sp W12Sp W12
Sp W12
 
Presentation w6
Presentation w6Presentation w6
Presentation w6
 
W11 b
W11 bW11 b
W11 b
 
Sp W10
Sp W10Sp W10
Sp W10
 

Similar to TDD of HTTP Clients With WebMock

Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...
Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...
Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...confluent
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineAndy McKay
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...DynamicInfraDays
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express MiddlewareMorris Singer
 
Finch.io - Purely Functional REST API with Finagle
Finch.io - Purely Functional REST API with FinagleFinch.io - Purely Functional REST API with Finagle
Finch.io - Purely Functional REST API with FinagleVladimir Kostyukov
 
CouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 HourCouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 HourPeter Friese
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on AndroidSven Haiges
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftChris Bailey
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasminePaulo Ragonha
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCLFastly
 
RESTful API In Node Js using Express
RESTful API In Node Js using Express RESTful API In Node Js using Express
RESTful API In Node Js using Express Jeetendra singh
 
Full stack development with node and NoSQL - All Things Open - October 2017
Full stack development with node and NoSQL - All Things Open - October 2017Full stack development with node and NoSQL - All Things Open - October 2017
Full stack development with node and NoSQL - All Things Open - October 2017Matthew Groves
 
Full Stack Development with Node.js and NoSQL
Full Stack Development with Node.js and NoSQLFull Stack Development with Node.js and NoSQL
Full Stack Development with Node.js and NoSQLAll Things Open
 

Similar to TDD of HTTP Clients With WebMock (20)

Server Side Swift: Vapor
Server Side Swift: VaporServer Side Swift: Vapor
Server Side Swift: Vapor
 
Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...
Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...
Using Location Data to Showcase Keys, Windows, and Joins in Kafka Streams DSL...
 
Intro to Sail.js
Intro to Sail.jsIntro to Sail.js
Intro to Sail.js
 
huhu
huhuhuhu
huhu
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
Finch.io - Purely Functional REST API with Finagle
Finch.io - Purely Functional REST API with FinagleFinch.io - Purely Functional REST API with Finagle
Finch.io - Purely Functional REST API with Finagle
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
CouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 HourCouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 Hour
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) Swift
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
Os Pruett
Os PruettOs Pruett
Os Pruett
 
RESTful API In Node Js using Express
RESTful API In Node Js using Express RESTful API In Node Js using Express
RESTful API In Node Js using Express
 
Full stack development with node and NoSQL - All Things Open - October 2017
Full stack development with node and NoSQL - All Things Open - October 2017Full stack development with node and NoSQL - All Things Open - October 2017
Full stack development with node and NoSQL - All Things Open - October 2017
 
Full Stack Development with Node.js and NoSQL
Full Stack Development with Node.js and NoSQLFull Stack Development with Node.js and NoSQL
Full Stack Development with Node.js and NoSQL
 

Recently uploaded

Connecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKConnecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKUXDXConf
 
Top 10 Symfony Development Companies 2024
Top 10 Symfony Development Companies 2024Top 10 Symfony Development Companies 2024
Top 10 Symfony Development Companies 2024TopCSSGallery
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Julian Hyde
 
Syngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdfSyngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdfSyngulon
 
Strategic AI Integration in Engineering Teams
Strategic AI Integration in Engineering TeamsStrategic AI Integration in Engineering Teams
Strategic AI Integration in Engineering TeamsUXDXConf
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfFIDO Alliance
 
Structuring Teams and Portfolios for Success
Structuring Teams and Portfolios for SuccessStructuring Teams and Portfolios for Success
Structuring Teams and Portfolios for SuccessUXDXConf
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxDavid Michel
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastUXDXConf
 
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdfHow Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdfFIDO Alliance
 
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...FIDO Alliance
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfFIDO Alliance
 
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...CzechDreamin
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyJohn Staveley
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...FIDO Alliance
 
Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024Patrick Viafore
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?Mark Billinghurst
 
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCustom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCzechDreamin
 
What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024Stephanie Beckett
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfSrushith Repakula
 

Recently uploaded (20)

Connecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKConnecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAK
 
Top 10 Symfony Development Companies 2024
Top 10 Symfony Development Companies 2024Top 10 Symfony Development Companies 2024
Top 10 Symfony Development Companies 2024
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
 
Syngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdfSyngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdf
 
Strategic AI Integration in Engineering Teams
Strategic AI Integration in Engineering TeamsStrategic AI Integration in Engineering Teams
Strategic AI Integration in Engineering Teams
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
Structuring Teams and Portfolios for Success
Structuring Teams and Portfolios for SuccessStructuring Teams and Portfolios for Success
Structuring Teams and Portfolios for Success
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at Comcast
 
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdfHow Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
 
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
 
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John Staveley
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCustom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
 
What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 

TDD of HTTP Clients With WebMock

  • 1. TDD of HTTP Clients with WebMock Bartosz Blimke @bartoszblimke new bamboo
  • 2. Test TDD RED RED Code new bamboo
  • 3. Hello http://my.geoservice.org Give me location for zip code WC1H 9EF GET http://my.geoservice.org/?zip=WC1H 9EF HTTP 200 - OK Content-Type: application/json Here is your location: {"latitude"=>"51.52","longitude":-0.12"} new bamboo
  • 4. describe GeoCoordinatesFinder do it "should make http request to geocoding service" do end it "should report coordinates provided by geocoding service" do end end new bamboo
  • 5. describe GeoCoordinatesFinder do it "should make http request to geocoding service" do ??? end it "should report coordinates provided by geocoding service" do ??? end end new bamboo
  • 8. class GeoCoordinatesFinder def get_coordinates_by_zip_code(zip) res = Net::HTTP.start("my.geoservice.org", 80) { |http| http.get("/?zip=#{URI.encode(zip)}") } result = JSON.parse(res) [result['latitude'], result['longitude']] end end new bamboo
  • 9. describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new @mock_http = mock("http") @res = mock(Net::HTTPOK, :body => "{"latitude":"5.5","longitude":"6.6"}") Net::HTTP.stub!(:start).with("my.geoservice.org", 80).and_yield @mock_http @mock_http.stub!(:get).with("/?zip=WC1H%209EF").and_return(@res) end it "should make http request to geocoding service" do Net::HTTP.should_receive(:start).and_yield @mock_http @mock_http.should_receive(:get).with("/?zip=WC1H%209EF") @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").should == ["5.5", "6.6"] end end new bamboo
  • 11. class GeoCoordinatesFinder def get_coordinates_by_zip_code(zip) url = "http://my.geoservice.org?zip=#{URI.encode(zip)}" response = RestClient.get(url) result = JSON.parse(response) [result['latitude'], result['longitude']] end end new bamboo
  • 13. describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new RestClient.stub!(:get).with("http://my.geoservice.org?zip=WC1H%209EF"). and_return("{"latitude":"5.5","longitude":"6.6"}") end it "should make http request to yahoo geocoding service" do RestClient.should_receive(:get).with("http://my.geoservice.org?zip=WC1H%209EF") @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF").should == ["5.5", "6.6"] end end new bamboo
  • 14. I wanted a tool for stubbing HTTP requests that...
  • 15. I wanted a tool for stubbing HTTP requests that... • doesn’t depend on usage of a specific API
  • 16. I wanted a tool for stubbing HTTP requests that... • doesn’t depend on usage of a specific API • supports stubbing based on request method, uri, body and headers
  • 17. I wanted a tool for stubbing HTTP requests that... • doesn’t depend on usage of a specific API • supports stubbing based on request method, uri, body and headers • verifies expectations of requests
  • 18. I wanted a tool for stubbing HTTP requests that... • doesn’t depend on usage of a specific API • supports stubbing based on request method, uri, body and headers • verifies expectations of requests • supports different HTTP libraries
  • 19. WebMock new bamboo
  • 20. require 'webmock/rspec' include WebMock describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end it "should make http request to geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF") end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"). should == ["5.5", "6.6"] end end new bamboo
  • 21. require 'webmock/rspec' include WebMock describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end it "should make http request to geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF") end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"). should == ["5.5", "6.6"] end end new bamboo
  • 22. require 'webmock/rspec' include WebMock describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end it "should make http request to geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") WebMock.should have_requested(:get, "http://my.geoservice.org?zip=WC1H 9EF") end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"). should == ["5.5", "6.6"] end end new bamboo
  • 23. require 'webmock/rspec' include WebMock describe GeoCoordinatesFinder do before(:each) do @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end it "should make http request to geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") request(:get, "http://my.geoservice.org?zip=WC1H 9EF").should have_been_made end it "should report coordinates provided by geocoding service" do @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"). should == ["5.5", "6.6"] end end new bamboo
  • 25. class GeoCoordinatesFinder def get_coordinates_by_zip_code(zip) uri = "http://my_geoservice.org?zip=#{URI.encode(zip)}" result = JSON.parse(HTTPClient.new.get(uri).content) [result['latitude'], result['longitude']] end end new bamboo
  • 27. Test::Unit require 'webmock/test_unit' include WebMock class TestGeoCoordinatesFinder < Test::Unit::TestCase def setup @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end def test_making_request_to_geocoding_service @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") assert_requested :get, "http://my.geoservice.org?zip=WC1H 9EF" end def test_reporting_coordinates_provided_by_geocoding_service assert_equal @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"), ["5.5", "6.6"] end end new bamboo
  • 28. Test::Unit require 'webmock/test_unit' include WebMock class TestGeoCoordinatesFinder < Test::Unit::TestCase def setup @coordinates_finder = GeoCoordinatesFinder.new stub_request(:get, "http://my.geoservice.org?zip=WC1H 9EF"). to_return(:body => "{"latitude":"5.5","longitude":"6.6"}") end def test_making_request_to_geocoding_service @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF") assert_requested :get, "http://my.geoservice.org?zip=WC1H 9EF" end def test_reporting_coordinates_provided_by_geocoding_service assert_equal @coordinates_finder.get_coordinates_by_zip_code("WC1H 9EF"), ["5.5", "6.6"] end end new bamboo
  • 29. Cucumber features/common/env.rb require 'webmock' include WebMock Before do WebMock.disable_net_connect! WebMock.reset_webmock end new bamboo
  • 30. Cucumber features/finding_geo_coordinates.feature Feature: Finding the way to an awesome conference In order to find the best way to Ruby Manor As a ruby geek I want to see how to get there on a map Scenario: User finds zip code coordinates on a map Given geoservice reports coordinates "51.52, -0.12" for zip code "WC1H 9EF" When I type "WC1H 9EF" into zip code field And I press "Show on map" Then I should see map centered at latitude "51.52" and longitude "-0.12" new bamboo
  • 31. Cucumber features/step_definitions/geocoding_service_steps.rb Given /^geoservice reports (.+), (.+) as coordinates for zip code (.+)$/ do |lat, long, zip_code| stub_request(:get, "http://my.geoservice.org?zip=#{zip_code}"). to_return( :body => "{"latitude":"#{lat}","longitude":"#{long}"}" ) end new bamboo
  • 32. Matching Requests on Body and Headers stub_request(:post, "www.example.net"). with(:body => "abc", :headers => { :content_type => 'text/plain' }) RestClient.post "www.example.net", "abc", :content_type => 'text/plain' WebMock.should have_requested(:post, "www.example.net"). with(:body => "abc", :headers => { 'Content-Type' => 'text/plain' }) new bamboo
  • 33. Smart URI Matching www.example.net/my path?x=my params new bamboo
  • 34. Smart URI Matching www.example.net/my path?x=my params www.example.net/my%20path?x=my%20params www.example.net:80/my path?x=my params www.example.net:80/my%20path?x=my%20params http://www.example.net/my path?x=my params http://www.example.net/my%20path?x=my%20params http://www.example.net:80/my path?x=my params http://www.example.net:80/my%20path?x=my%20params new bamboo
  • 35. Matching URIs with Regexp stub_request(:post, /example/). with(:body => "abc", :headers => { :content_type => 'text/plain' }) RestClient.post "www.example.net", "abc", :content_type => 'text/plain' WebMock.should have_requested(:post, /example/). with(:body => "abc", :headers => { 'Content-Type' => 'text/plain' }) new bamboo
  • 36. Basic Authentication stub_request(:any, 'user:pass@www.example.net'). to_return(:body => 'abc') RestClient::Resource.new( 'www.example.net', :user => 'user', :password => 'pass' ).post('abc') # ===> "abcn" WebMock.should have_requested(:post, 'user:pass@www.example.net') new bamboo
  • 37. Dynamic Responses stub_request(:any, 'www.example.net'). to_return(:body => lambda { |request| request.body }) RestClient.post('www.example.net', 'abc') # ===> "abcn" new bamboo
  • 38. Supported Libraries Net::HTTP HTTPClient new bamboo
  • 39. Supported Libraries Net::HTTP HTTPClient HTTParty RESTClient RightScale::HttpConnection open-uri new bamboo

Editor's Notes

  1. Hi. I&amp;#x2019;m Bartosz Blimke. I&amp;#x2019;m work for New Bamboo, a development shop. We are working in a rigorous agile environment and we also offer agile coaching. I&amp;#x2019;ve been working in Extreme Programming environments for the last 5 years, on small projects as well as on very big corporate projects. One of the code XP practices is ....
  2. is test driven development. I&amp;#x2019;m doing TDD for about 3 or 4 years now.
  3. none of the tools I found offered these features, and non of the tools I found were easily extandable to implement these features
  4. none of the tools I found offered these features, and non of the tools I found were easily extandable to implement these features
  5. none of the tools I found offered these features, and non of the tools I found were easily extandable to implement these features
  6. none of the tools I found offered these features, and non of the tools I found were easily extandable to implement these features