fakettp 0.2.4.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/README.rdoc +201 -0
- data/Rakefile +113 -0
- data/VERSION +1 -0
- data/features/control.feature +12 -0
- data/features/dashboard.feature +38 -0
- data/features/expectations.feature +27 -0
- data/features/expectations/expect_get +3 -0
- data/features/expectations/get_foo +3 -0
- data/features/expectations/get_root +3 -0
- data/features/expectations/pass_and_fail +5 -0
- data/features/expectations/set_response +5 -0
- data/features/step_definitions/expectations.rb +5 -0
- data/features/step_definitions/http.rb +42 -0
- data/features/step_definitions/simulator.rb +25 -0
- data/features/step_definitions/webrat_steps.rb +99 -0
- data/features/support/env.rb +5 -0
- data/features/support/fakettp.rb +2 -0
- data/features/support/paths.rb +12 -0
- data/features/support/xpath.rb +10 -0
- data/features/verification.feature +49 -0
- data/lib/fakettp/commands/fakettp_command.rb +31 -19
- data/lib/fakettp/controller.rb +12 -0
- data/lib/fakettp/db.rb +7 -0
- data/lib/fakettp/error.rb +6 -18
- data/lib/fakettp/expectation.rb +28 -44
- data/lib/fakettp/fakettp.yml +3 -0
- data/lib/fakettp/public/fakettp.css +33 -0
- data/lib/fakettp/schema.rb +14 -0
- data/lib/fakettp/simulator.rb +16 -10
- data/lib/fakettp/views/index.erb +14 -0
- data/spec/fakettp/commands/fakettp_command_spec.rb +128 -0
- data/spec/fakettp/controller_spec.rb +250 -0
- data/spec/fakettp/error_spec.rb +40 -0
- data/spec/fakettp/expectation_helper_spec.rb +33 -0
- data/spec/fakettp/expectation_spec.rb +142 -0
- data/spec/fakettp/simulator_spec.rb +149 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +18 -0
- metadata +73 -24
- data/README.html +0 -113
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
= FakeTTP
|
2
|
+
|
3
|
+
== Purpose
|
4
|
+
|
5
|
+
When you are writing acceptance/integration tests for an application which
|
6
|
+
makes HTTP requests to a remote application, sometimes you need to be able
|
7
|
+
to test the interactions in different scenarios without talking to a real
|
8
|
+
instance of the remote application.
|
9
|
+
|
10
|
+
FakeTTP is a standalone web application that allows you to mock requests (ie
|
11
|
+
set and verify expectations on the requests your application makes, and
|
12
|
+
return suitable responses to those requests).
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
=== Install the gem
|
17
|
+
|
18
|
+
Add GemCutter as a gem source (you only need to do this once):
|
19
|
+
|
20
|
+
gem sources -a http://gemcutter.org
|
21
|
+
|
22
|
+
Then install FakeTTP:
|
23
|
+
|
24
|
+
sudo gem install fakettp
|
25
|
+
|
26
|
+
Alternatively, you can specify the source when you install the gem:
|
27
|
+
|
28
|
+
sudo gem install fakettp --source http://gemcutter.org
|
29
|
+
|
30
|
+
=== Create a FakeTTP directory
|
31
|
+
|
32
|
+
You can install FakeTTP anywhere that your web server user can see it:
|
33
|
+
|
34
|
+
fakettp install <directory> <hostname>
|
35
|
+
|
36
|
+
Set _hostname_ to the host you want to use for FakeTTP control requests (the
|
37
|
+
examples below use <tt>fakettp.local</tt>). You can install multiple copies
|
38
|
+
and run them independently by using different directories and hostnames (for
|
39
|
+
example on a CI server to prevent clashes when building multiple projects
|
40
|
+
in parallel).
|
41
|
+
|
42
|
+
=== Point your web server at the directory
|
43
|
+
|
44
|
+
FakeTTP should work with any Rack-compatible server: just point the server to
|
45
|
+
the correct directory. For example, using Passenger[http://www.modrails.com/]
|
46
|
+
(mod_rails) with Apache, create a virtual host along these lines:
|
47
|
+
|
48
|
+
<VirtualHost *:80>
|
49
|
+
ServerName fakettp.local
|
50
|
+
DocumentRoot "/path/to/fakettp/public"
|
51
|
+
<directory "/path/to/fakettp/public">
|
52
|
+
Order deny,allow
|
53
|
+
Deny from all
|
54
|
+
Allow from 127.0.0.1
|
55
|
+
</directory>
|
56
|
+
</VirtualHost>
|
57
|
+
|
58
|
+
Then make sure <tt>fakettp.local</tt> resolves to 127.0.0.1 (assuming you're
|
59
|
+
running the simulator on the same machine as the application under test), eg
|
60
|
+
by adding the following line to <tt>/etc/hosts</tt>:
|
61
|
+
|
62
|
+
127.0.0.1 fakettp.local
|
63
|
+
|
64
|
+
=== IMPORTANT: security note
|
65
|
+
|
66
|
+
Because expectations are set by posting Ruby code to be executed on the
|
67
|
+
server, you probably don't want any old Tom, Dick or Harry to be able to
|
68
|
+
connect. The security settings in the virtual host config example above
|
69
|
+
restrict access to clients running on the local machine.
|
70
|
+
|
71
|
+
== Usage
|
72
|
+
|
73
|
+
The examples below assume you've installed using the hostname
|
74
|
+
<tt>fakettp.local</tt>. If you've used a different host, adjust appropriately.
|
75
|
+
|
76
|
+
=== Resetting
|
77
|
+
|
78
|
+
To reset FakeTTP (ie remove all expectations and errors), make an HTTP POST
|
79
|
+
request to <tt>http://fakettp.local/reset</tt>.
|
80
|
+
|
81
|
+
==== Calling from curl
|
82
|
+
|
83
|
+
curl fakettp.local/reset -X POST
|
84
|
+
|
85
|
+
=== Setting expectations
|
86
|
+
|
87
|
+
To create a new expectation, make an HTTP POST request to
|
88
|
+
<tt>http://fakettp.local/expect</tt>, with a <em>Content-Type</em> header of
|
89
|
+
'text/plain' and the request data containing a Ruby block to execute.
|
90
|
+
|
91
|
+
The supplied code should be in the following format, and will generally
|
92
|
+
consist of a number of assertions on the request, followed by creation of
|
93
|
+
the response to return to the application under test.
|
94
|
+
|
95
|
+
expect "GET of /foo" do
|
96
|
+
request.host.should == 'fakettp.local'
|
97
|
+
request.path_info.should == '/foo'
|
98
|
+
|
99
|
+
content_type 'text/plain'
|
100
|
+
"All is well\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
The label on the first line is used in error reporting.
|
104
|
+
|
105
|
+
The expectation code has access to the underlying Sinatra request and
|
106
|
+
response objects etc, as well as RSpec[http://rspec.info] matchers.
|
107
|
+
|
108
|
+
==== Calling from curl
|
109
|
+
|
110
|
+
Assuming the expectation is in <tt>expectation_file</tt>:
|
111
|
+
|
112
|
+
curl -X POST fakettp.local/expect -X POST -H 'Content-Type:text/plain' --binary-data <expectation_file>
|
113
|
+
|
114
|
+
=== Verifying
|
115
|
+
|
116
|
+
To verify that all expectations have been met, make an HTTP GET request
|
117
|
+
to <tt>http://fakettp.local/verify</tt>.
|
118
|
+
|
119
|
+
If all is well, the response will be a <em>200 OK</em> with a body of 'OK'. Otherwise
|
120
|
+
the status will be <em>400 Bad Request</em>, with a list of failures in the body. The
|
121
|
+
failure messages include the complete details of the unexpected request that
|
122
|
+
was received, to assist debugging.
|
123
|
+
|
124
|
+
==== Calling from curl
|
125
|
+
|
126
|
+
curl fakettp.local/verify
|
127
|
+
|
128
|
+
=== Web console
|
129
|
+
|
130
|
+
Point your browser at http://fakettp.local/
|
131
|
+
|
132
|
+
Currently this is very basic, just showing the expectations from the last run.
|
133
|
+
|
134
|
+
=== Multiple faked hosts
|
135
|
+
|
136
|
+
To have FakeTTP respond to multiple hostnames, create the appropriate hosts
|
137
|
+
entries. If you're using name-based virtual hosts in Apache, add a
|
138
|
+
_ServerAlias_ entry to the virtual host config, under the _ServerName_ line, eg:
|
139
|
+
|
140
|
+
ServerAlias foo.com bar.com
|
141
|
+
|
142
|
+
== Change log
|
143
|
+
|
144
|
+
0.3.0 (18 May 2009)
|
145
|
+
|
146
|
+
* Now uses SQLite and ActiveRecord to store expectations, instead of the filesystem.
|
147
|
+
* Supports specification of hostname on installation
|
148
|
+
* HTML page showing expectations from the last run
|
149
|
+
|
150
|
+
0.2.4.1 (5 May 2009)
|
151
|
+
|
152
|
+
* Temporarily depend on edge version of Sinatra, to gain Rack 1.0 compatibility.
|
153
|
+
|
154
|
+
0.2.4 (25 Mar 2009)
|
155
|
+
|
156
|
+
* Fixed a bug which caused expectations to be run in the wrong order if there
|
157
|
+
were more than nine of them.
|
158
|
+
|
159
|
+
0.2.3 (19 Mar 2009)
|
160
|
+
|
161
|
+
* Fixed a bug where expectations were being overwritten on Linux due to
|
162
|
+
Dir.entries not returning results in the expected order
|
163
|
+
|
164
|
+
0.2.2 (18 Mar 2009)
|
165
|
+
|
166
|
+
* Only accept control requests (reset, expect, verify) on fakettp.local
|
167
|
+
|
168
|
+
0.2.1 (17 Mar 2009)
|
169
|
+
|
170
|
+
* Fixed issue where rspec matchers weren't available to expectations
|
171
|
+
|
172
|
+
0.2 (14 Mar 2009)
|
173
|
+
|
174
|
+
* Complete rewrite, with tests and classes and stuff this time.
|
175
|
+
|
176
|
+
If you get an 'unexpected return' error, remove the return statement from your
|
177
|
+
expectation, and just put the return value on the last line of the _expect_ block.
|
178
|
+
|
179
|
+
0.1.2 (13 Feb 2009)
|
180
|
+
|
181
|
+
* Make sure README.html appears in generated gem
|
182
|
+
|
183
|
+
0.1.1 (13 Feb 2009)
|
184
|
+
|
185
|
+
* Fix permissions on installed tmp directory.
|
186
|
+
|
187
|
+
0.1.0 (13 Feb 2009)
|
188
|
+
|
189
|
+
* First release as a gem.
|
190
|
+
|
191
|
+
== To Do
|
192
|
+
|
193
|
+
* Add examples
|
194
|
+
* Make control requests RESTful?
|
195
|
+
* Show label in verification error for expected requests that weren't received
|
196
|
+
* Add facility to stub as well as mock requests
|
197
|
+
* Allow more flexibility in request ordering
|
198
|
+
* Allow user-specific helper files in installation dir
|
199
|
+
* Provide Ruby API to set expectations etc
|
200
|
+
* Return the expected content type even if expectation fails
|
201
|
+
* Highlight failed lines in console
|
data/Rakefile
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'spec/rake/verify_rcov'
|
5
|
+
|
6
|
+
desc 'run specs, create gem, install and test'
|
7
|
+
task :default => [:rcov, :verify_rcov, :gemspec, :build, :local_install, :test_install, :'cucumber:all', :ok]
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'jeweler'
|
11
|
+
Jeweler::Tasks.new do |gem|
|
12
|
+
gem.name = "fakettp"
|
13
|
+
gem.summary = %Q{HTTP server mocking tool}
|
14
|
+
gem.description = gem.summary
|
15
|
+
gem.email = "kerryjbuckley@gmail.com"
|
16
|
+
gem.homepage = "http://github.com/kerryb/fakettp"
|
17
|
+
gem.authors = ["Kerry Buckley"]
|
18
|
+
gem.add_dependency 'sinatra-sinatra'
|
19
|
+
gem.add_dependency 'sqlite3-ruby'
|
20
|
+
gem.add_development_dependency 'jeweler'
|
21
|
+
gem.add_development_dependency 'rcov'
|
22
|
+
gem.add_development_dependency "rspec"
|
23
|
+
gem.add_development_dependency "cucumber"
|
24
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
25
|
+
end
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
rescue LoadError
|
28
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'spec/rake/spectask'
|
32
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
33
|
+
spec.libs << 'lib' << 'spec'
|
34
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
38
|
+
spec.libs << 'lib' << 'spec'
|
39
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
40
|
+
spec.rcov = true
|
41
|
+
spec.rcov_opts = ['--exclude', 'spec']
|
42
|
+
end
|
43
|
+
|
44
|
+
task :spec => :check_dependencies
|
45
|
+
|
46
|
+
desc 'Check spec coverage'
|
47
|
+
RCov::VerifyTask.new(:verify_rcov) do |t|
|
48
|
+
t.threshold = 100.0
|
49
|
+
t.index_html = 'coverage/index.html'
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
require 'cucumber/rake/task'
|
54
|
+
namespace :cucumber do
|
55
|
+
Cucumber::Rake::Task.new({:ok => :check_dependencies}, 'Run features that should pass') do |t|
|
56
|
+
t.cucumber_opts = "--color --tags ~@wip --strict --format 'pretty'"
|
57
|
+
end
|
58
|
+
|
59
|
+
Cucumber::Rake::Task.new({:wip => :check_dependencies}, 'Run features that are being worked on') do |t|
|
60
|
+
t.cucumber_opts = "--color --tags @wip:2 --wip --format 'pretty'"
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'Run all features'
|
64
|
+
task :all => [:ok, :wip]
|
65
|
+
end
|
66
|
+
desc 'Alias for cucumber:ok'
|
67
|
+
task :cucumber => 'cucumber:ok'
|
68
|
+
rescue LoadError
|
69
|
+
task :cucumber do
|
70
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
require 'rake/rdoctask'
|
75
|
+
Rake::RDocTask.new do |rdoc|
|
76
|
+
if File.exist?('VERSION')
|
77
|
+
version = File.read('VERSION')
|
78
|
+
else
|
79
|
+
version = ""
|
80
|
+
end
|
81
|
+
|
82
|
+
rdoc.rdoc_dir = 'rdoc'
|
83
|
+
rdoc.title = "fakettp2 #{version}"
|
84
|
+
rdoc.rdoc_files.include('README*')
|
85
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
86
|
+
end
|
87
|
+
|
88
|
+
desc 'remove all build products'
|
89
|
+
task :clean do
|
90
|
+
FileUtils.rm_rf %w(tmp install coverage pkg)
|
91
|
+
end
|
92
|
+
|
93
|
+
task :local_install do
|
94
|
+
system 'sudo gem in -l pkg/*'
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'Install FakeTTP into local install directory'
|
98
|
+
task :test_install do
|
99
|
+
rm_rf 'install'
|
100
|
+
system 'fakettp install install fakettp.local'
|
101
|
+
touch 'install/tmp/restart.txt'
|
102
|
+
end
|
103
|
+
|
104
|
+
task :ok do
|
105
|
+
red = "\e[31m"
|
106
|
+
yellow = "\e[33m"
|
107
|
+
green = "\e[32m"
|
108
|
+
blue = "\e[34m"
|
109
|
+
purple = "\e[35m"
|
110
|
+
bold = "\e[1m"
|
111
|
+
normal = "\e[0m"
|
112
|
+
puts "", "#{bold}#{red}*#{yellow}*#{green}*#{blue}*#{purple}* #{green} ALL TESTS PASSED #{purple}*#{blue}*#{green}*#{yellow}*#{red}*#{normal}"
|
113
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Controlling the simulator
|
2
|
+
Scenario: Attempting to reset the simulator using the wrong host
|
3
|
+
When we post to /reset on foo.fake.local
|
4
|
+
Then the response body should contain 'Simulator received mismatched request'
|
5
|
+
|
6
|
+
Scenario: Attempting to create an expectation using the wrong host
|
7
|
+
When we post to /expect on foo.fake.local
|
8
|
+
Then the response body should contain 'Simulator received mismatched request'
|
9
|
+
|
10
|
+
Scenario: Attempting to verify the simulator using the wrong host
|
11
|
+
When we get /verify on foo.fake.local
|
12
|
+
Then the response body should contain 'Simulator received mismatched request'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Feature: Dashboard for debugging failures
|
2
|
+
Scenario: Dashboard web page served from /
|
3
|
+
When we get / on fakettp.local
|
4
|
+
Then the response should have a content type of 'text/html'
|
5
|
+
And /html/head/title in the response should be 'FakeTTP'
|
6
|
+
|
7
|
+
Scenario: Expectations are listed
|
8
|
+
Given the simulator is reset
|
9
|
+
And there are 3 expectations
|
10
|
+
When we get / on fakettp.local
|
11
|
+
Then there should be 3 /html/body/div elements in the response
|
12
|
+
|
13
|
+
Scenario: Show expectation heading and contents
|
14
|
+
Given the simulator is reset
|
15
|
+
And we expect get_root
|
16
|
+
When we get / on fakettp.local
|
17
|
+
Then //h1[1] in the response should be '1'
|
18
|
+
And //div[1]/pre in the response should be:
|
19
|
+
"""
|
20
|
+
expect "GET /" do
|
21
|
+
request.path_info.should == '/'
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
|
25
|
+
@wip
|
26
|
+
Scenario: Highlight passed and failed lines
|
27
|
+
Given the simulator is reset
|
28
|
+
And we expect pass_and_fail
|
29
|
+
And we get / on foo.fake.local
|
30
|
+
When we get / on fakettp.local
|
31
|
+
Then //div[1]/pre in the response should be:
|
32
|
+
"""
|
33
|
+
<span class="pass">expect "pass and fail" do
|
34
|
+
(2 + 2).should == 4</span>
|
35
|
+
<span class="fail"> true.should be_false</span>
|
36
|
+
'will never get here'
|
37
|
+
end
|
38
|
+
"""
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Expectations
|
2
|
+
Scenario: Using rspec matchers
|
3
|
+
Given the simulator is reset
|
4
|
+
And we expect expect_get
|
5
|
+
And we get / on foo.fake.local
|
6
|
+
Then verifying the simulator should report success
|
7
|
+
|
8
|
+
Scenario: Setting response headers
|
9
|
+
Given the simulator is reset
|
10
|
+
And we expect set_response
|
11
|
+
And we get / on foo.fake.local
|
12
|
+
Then the response should have a 'foo' header with a value of 'bar'
|
13
|
+
And verifying the simulator should report success
|
14
|
+
|
15
|
+
Scenario: Setting response content-type
|
16
|
+
Given the simulator is reset
|
17
|
+
And we expect set_response
|
18
|
+
And we get / on foo.fake.local
|
19
|
+
Then the response should have a content type of 'application/xml'
|
20
|
+
And verifying the simulator should report success
|
21
|
+
|
22
|
+
Scenario: Setting response code
|
23
|
+
Given the simulator is reset
|
24
|
+
And we expect set_response
|
25
|
+
And we get / on foo.fake.local
|
26
|
+
Then the response body should be '<foo />'
|
27
|
+
And verifying the simulator should report success
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
3
|
+
When /^we get (\S*) on (\S*)$/ do |path, host|
|
4
|
+
req = Net::HTTP::Get.new path
|
5
|
+
@@response = Net::HTTP.new(host).start {|http| http.request(req) }
|
6
|
+
end
|
7
|
+
When /^we post to (\S*) on (\S*)$/ do |path, host|
|
8
|
+
req = Net::HTTP::Post.new path
|
9
|
+
@@response = Net::HTTP.new(host).start {|http| http.request(req) }
|
10
|
+
end
|
11
|
+
|
12
|
+
Then /^the response should have a '(.*)' header with a value of '(.*)'$/ do |name, value|
|
13
|
+
@@response[name].should == value
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^the response should have a content type of '(.*)'$/ do |value|
|
17
|
+
@@response.content_type.should == value
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^the response body should be '(.*)'$/ do |value|
|
21
|
+
@@response.body.should == value
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^the response body should contain '(.*)'$/ do |value|
|
25
|
+
@@response.body.should include(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
Then /^(\S*) in the response should be '(.*)'$/ do |locator, value|
|
29
|
+
check_value locator, value
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^(\S*) in the response should be:$/ do |locator, value|
|
33
|
+
check_value locator, value
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_value locator, value
|
37
|
+
(Hpricot(@@response.body)/locator).inner_html.should == value
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^there should be (\d*) (.*) elements in the response$/ do |count, locator|
|
41
|
+
Hpricot(@@response.body).search(locator).size.should == count.to_i
|
42
|
+
end
|