flunk 0.0.1
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/LICENSE +19 -0
- data/README.md +298 -0
- data/Rakefile +8 -0
- data/flunk.gemspec +14 -0
- data/lib/flunk.rb +100 -0
- data/lib/generators/flunk_test/USAGE +8 -0
- data/lib/generators/flunk_test/flunk_test_generator.rb +7 -0
- data/lib/generators/flunk_test/templates/flunk_test.rb +29 -0
- data/lib/tasks/flunk.rake +2 -0
- data/test/test_flunk.rb +6 -0
- metadata +56 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Mysterious Trousers, LLC (http://www.mysterioustrousers.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
Flunk
|
2
|
+
=====
|
3
|
+
|
4
|
+
A gem for testing a Ruby on Rails web APIs by simulating a client.
|
5
|
+
|
6
|
+
|
7
|
+
### Installation
|
8
|
+
|
9
|
+
In your Gemfile, add this line:
|
10
|
+
|
11
|
+
gem "flunk"
|
12
|
+
|
13
|
+
### Description
|
14
|
+
|
15
|
+
We write mostly JSON APIs using Rails, not your traditional web app, so we wanted a better way to test our JSON APIs. This is flunk.
|
16
|
+
|
17
|
+
### Usage
|
18
|
+
|
19
|
+
In each test block you call a series of methods [`desc`, `path`, `method`, `username`, `password`, `body`, `status`, `assertions`] as necessary.
|
20
|
+
|
21
|
+
`desc`: In the future, a documentation generator will be added and this will be used to determine if the test should be documented as an API method.
|
22
|
+
`path`: The relative URL for the resource.
|
23
|
+
`method`: :get, :post, :put or :delete
|
24
|
+
`username`: For authentication using basic auth.
|
25
|
+
`password`: For authentication using basic auth.
|
26
|
+
`body`: The body of the request.
|
27
|
+
`status`: This method actually acts like an assertion. It is what the status of the response SHOULD be. An error will be thrown if it doesn't match.
|
28
|
+
`assertions`: A block of of assertions you call to verify the response was what it should have been.
|
29
|
+
|
30
|
+
Once you call `assertions`, the request is fired and a `result` method is available within the assertions block containing the response.
|
31
|
+
|
32
|
+
### Example
|
33
|
+
|
34
|
+
It's your typical rails integration test, but inherits from Flunk:
|
35
|
+
|
36
|
+
class UsersTest < Flunk
|
37
|
+
|
38
|
+
setup do
|
39
|
+
@user = FactoryGirl.create(:user)
|
40
|
+
end
|
41
|
+
|
42
|
+
You write tests that SHOULD pass to test your app's basic functionality all works:
|
43
|
+
|
44
|
+
test "Create User" do
|
45
|
+
desc "Creating a new Langwich user."
|
46
|
+
path "signup"
|
47
|
+
method :post
|
48
|
+
body user: attrs = FactoryGirl.attributes_for(:user)
|
49
|
+
status :created
|
50
|
+
assertions {
|
51
|
+
assert_equal result[:name], attrs[:name]
|
52
|
+
assert_equal result[:username], attrs[:username]
|
53
|
+
assert_equal result[:email], attrs[:email]
|
54
|
+
assert_not_nil result[:api_token]
|
55
|
+
user = User.find_by_api_token result[:api_token]
|
56
|
+
assert_equal 1, user.languages.count
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
test "Log In" do
|
61
|
+
desc "Obtain a users API token by logging in with their username and password"
|
62
|
+
path "login"
|
63
|
+
method :get
|
64
|
+
body username: @user.username, password: @user.password
|
65
|
+
status :ok
|
66
|
+
assertions {
|
67
|
+
assert_not_nil result[:api_token]
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
test "Log In With Email" do
|
72
|
+
path "login"
|
73
|
+
method :get
|
74
|
+
body username: @user.email, password: @user.password
|
75
|
+
status :ok
|
76
|
+
assertions {
|
77
|
+
assert_not_nil result[:api_token]
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
test "Read User" do
|
82
|
+
desc "Read a users information."
|
83
|
+
path "account"
|
84
|
+
method :get
|
85
|
+
username @user.api_token
|
86
|
+
status :ok
|
87
|
+
end
|
88
|
+
|
89
|
+
test "Update User" do
|
90
|
+
desc "Update the username, e-mail, password and/or name"
|
91
|
+
path "account"
|
92
|
+
method :put
|
93
|
+
username @user.api_token
|
94
|
+
body user: { username: username = Faker::Internet.user_name }
|
95
|
+
status :ok
|
96
|
+
assertions {
|
97
|
+
assert_equal result[:username], username
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
test "Update E-mail" do
|
102
|
+
path "account"
|
103
|
+
method :put
|
104
|
+
username @user.api_token
|
105
|
+
body user: { email: email = Faker::Internet.email }
|
106
|
+
status :ok
|
107
|
+
assertions {
|
108
|
+
assert_equal result[:email], email
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
test "Update User Password" do
|
113
|
+
path "account"
|
114
|
+
method :put
|
115
|
+
username @user.api_token
|
116
|
+
body user: { password: Faker::Lorem.characters(10) }
|
117
|
+
status :ok
|
118
|
+
end
|
119
|
+
|
120
|
+
test "Update Name" do
|
121
|
+
path "account"
|
122
|
+
method :put
|
123
|
+
username @user.api_token
|
124
|
+
body user: { name: name = Faker::Name.first_name }
|
125
|
+
status :ok
|
126
|
+
assertions {
|
127
|
+
assert_equal result[:name], name
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
test "Delete User" do
|
132
|
+
path "account"
|
133
|
+
method :delete
|
134
|
+
username @user.api_token
|
135
|
+
status :ok
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
Then, write tests that SHOULDN'T pass to make sure your app rejects bad requests correctly/gracefully:
|
141
|
+
|
142
|
+
|
143
|
+
flunk "Create User", "Missing username" do
|
144
|
+
desc "Attempting to create a user without a username."
|
145
|
+
path "signup"
|
146
|
+
method :post
|
147
|
+
body user: FactoryGirl.attributes_for(:user, username: nil)
|
148
|
+
status :unprocessable_entity
|
149
|
+
end
|
150
|
+
|
151
|
+
flunk "Create User", "Username already taken" do
|
152
|
+
path "signup"
|
153
|
+
method :post
|
154
|
+
body user: FactoryGirl.attributes_for(:user, username: @user.username)
|
155
|
+
status :unprocessable_entity
|
156
|
+
end
|
157
|
+
|
158
|
+
flunk "Create User", "Invalid username" do
|
159
|
+
path "signup"
|
160
|
+
method :post
|
161
|
+
body user: FactoryGirl.attributes_for(:user, username: "a234$2aa" )
|
162
|
+
status :unprocessable_entity
|
163
|
+
end
|
164
|
+
|
165
|
+
flunk "Create User", "Missing e-mail" do
|
166
|
+
desc "Attempting to create a user without a e-mail."
|
167
|
+
path "signup"
|
168
|
+
method :post
|
169
|
+
body user: FactoryGirl.attributes_for(:user, email: nil)
|
170
|
+
status :unprocessable_entity
|
171
|
+
end
|
172
|
+
|
173
|
+
flunk "Create User", "E-mail already taken" do
|
174
|
+
desc "Attempting to create a user with an e-mail that's already taken."
|
175
|
+
path "signup"
|
176
|
+
method :post
|
177
|
+
body user: FactoryGirl.attributes_for(:user, email: @user.email)
|
178
|
+
status :unprocessable_entity
|
179
|
+
end
|
180
|
+
|
181
|
+
flunk "Create User", "Invalid e-mail" do
|
182
|
+
path "signup"
|
183
|
+
method :post
|
184
|
+
body user: FactoryGirl.attributes_for(:user, email: "aaaa@aakk")
|
185
|
+
status :unprocessable_entity
|
186
|
+
end
|
187
|
+
|
188
|
+
flunk "Create User", "Missing password" do
|
189
|
+
desc "Attempting to create a user without a password."
|
190
|
+
path "signup"
|
191
|
+
method :post
|
192
|
+
body user: FactoryGirl.attributes_for(:user, password: nil)
|
193
|
+
status :unprocessable_entity
|
194
|
+
end
|
195
|
+
|
196
|
+
flunk "Create User", "Missing name" do
|
197
|
+
path "signup"
|
198
|
+
method :post
|
199
|
+
body user: FactoryGirl.attributes_for(:user, name: nil)
|
200
|
+
status :unprocessable_entity
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
flunk "Log In", "No username" do
|
207
|
+
desc "Attempting to obtain an API token with the wrong password"
|
208
|
+
path "login"
|
209
|
+
method :get
|
210
|
+
body password: "a"
|
211
|
+
status :unauthorized
|
212
|
+
end
|
213
|
+
|
214
|
+
flunk "Log In", "Wrong password" do
|
215
|
+
desc "Attempting to obtain an API token with the wrong password"
|
216
|
+
path "login"
|
217
|
+
method :get
|
218
|
+
body username: @user.username, password: "a"
|
219
|
+
status :unauthorized
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
flunk "Read User", "Wrong API token" do
|
226
|
+
path "login"
|
227
|
+
method :get
|
228
|
+
username "a"
|
229
|
+
status :unauthorized
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
flunk "Update User", "Wrong password" do
|
236
|
+
path "account"
|
237
|
+
method :put
|
238
|
+
username "a"
|
239
|
+
body user: FactoryGirl.attributes_for(:user)
|
240
|
+
status :unauthorized
|
241
|
+
end
|
242
|
+
|
243
|
+
flunk "Update User", "Username already taken" do
|
244
|
+
path "account"
|
245
|
+
method :put
|
246
|
+
username @user.api_token
|
247
|
+
u = FactoryGirl.create(:user)
|
248
|
+
body user: { username: u.username }
|
249
|
+
status :unprocessable_entity
|
250
|
+
end
|
251
|
+
|
252
|
+
flunk "Update User", "Invalid username" do
|
253
|
+
path "account"
|
254
|
+
method :put
|
255
|
+
username @user.api_token
|
256
|
+
body user: { username: "a234$2aa" }
|
257
|
+
status :unprocessable_entity
|
258
|
+
end
|
259
|
+
|
260
|
+
flunk "Update User", "E-mail already taken" do
|
261
|
+
desc "Attempting to update a user with an e-mail that's already taken."
|
262
|
+
path "account"
|
263
|
+
method :put
|
264
|
+
username @user.api_token
|
265
|
+
u = FactoryGirl.create(:user)
|
266
|
+
body user: { email: u.email }
|
267
|
+
status :unprocessable_entity
|
268
|
+
end
|
269
|
+
|
270
|
+
flunk "Update User", "Invalid e-mail" do
|
271
|
+
desc "Attempting to update the user with an invalid e-mail"
|
272
|
+
path "account"
|
273
|
+
method :put
|
274
|
+
username @user.api_token
|
275
|
+
body user: { email: "aaaa@aakk" }
|
276
|
+
status :unprocessable_entity
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
|
282
|
+
flunk "Delete User", "Wrong password" do
|
283
|
+
path "account"
|
284
|
+
method :delete
|
285
|
+
username "a"
|
286
|
+
body user: FactoryGirl.attributes_for(:user)
|
287
|
+
status :unauthorized
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
### Generator
|
293
|
+
|
294
|
+
To generate a flunk test:
|
295
|
+
|
296
|
+
rails g generate flunk_test User
|
297
|
+
|
298
|
+
This will create an integration test: test/integration/users_test.rb
|
data/Rakefile
ADDED
data/flunk.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "flunk"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = ["Adam Kirk"]
|
6
|
+
s.email = %q{atomkirk@gmail.com}
|
7
|
+
s.homepage = %q{https://github.com/mysterioustrousers/flunk}
|
8
|
+
s.summary = %q{A gem for testing a Ruby on Rails web APIs by simulating a client.}
|
9
|
+
s.description = %q{A gem for testing a Ruby on Rails web APIs by simulating a client.}
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n").reject {|path| path =~ /\.gitignore$/ }
|
12
|
+
s.test_files =`git ls-files -- test/*`.split("\n")
|
13
|
+
s.require_paths = ["lib"]
|
14
|
+
end
|
data/lib/flunk.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
class Flunk < ActionDispatch::IntegrationTest
|
2
|
+
|
3
|
+
def self.test(name, &block)
|
4
|
+
new_proc = Proc.new do
|
5
|
+
instance_eval(&block)
|
6
|
+
result
|
7
|
+
@assertions.call unless @assertions.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
super name, &new_proc
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.flunk(action, failure_reason, &block)
|
14
|
+
test("FLUNKED: #{action} (#{failure_reason})", &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def result
|
18
|
+
if !@result_fetched
|
19
|
+
@result_fetched = true
|
20
|
+
|
21
|
+
if (@username || @password)
|
22
|
+
@headers ||= {}
|
23
|
+
@headers["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64(@username.to_s + ":" + @password.to_s)}".strip
|
24
|
+
end
|
25
|
+
|
26
|
+
send @method, @path, @body, @headers
|
27
|
+
|
28
|
+
@response = response
|
29
|
+
|
30
|
+
assert_response @status
|
31
|
+
|
32
|
+
if response.body.length > 2
|
33
|
+
if response.content_type == 'application/json'
|
34
|
+
json = ActiveSupport::JSON.decode(response.body)
|
35
|
+
rec_symbolize( json )
|
36
|
+
@result = json
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@result
|
42
|
+
end
|
43
|
+
|
44
|
+
def path(path)
|
45
|
+
@path = path
|
46
|
+
end
|
47
|
+
|
48
|
+
def method(method)
|
49
|
+
@method = method
|
50
|
+
end
|
51
|
+
|
52
|
+
def username(username)
|
53
|
+
@username = username
|
54
|
+
end
|
55
|
+
|
56
|
+
def password(password)
|
57
|
+
@password = password
|
58
|
+
end
|
59
|
+
|
60
|
+
def ssl(ssl)
|
61
|
+
@ssl = ssl
|
62
|
+
end
|
63
|
+
|
64
|
+
def body(body)
|
65
|
+
@body = body
|
66
|
+
end
|
67
|
+
|
68
|
+
def header(key, value)
|
69
|
+
@headers ||= {}
|
70
|
+
@headers[key] = value
|
71
|
+
end
|
72
|
+
|
73
|
+
def param(key, value)
|
74
|
+
@params ||= {}
|
75
|
+
@params[key] = value
|
76
|
+
end
|
77
|
+
|
78
|
+
def status(status)
|
79
|
+
@status = status
|
80
|
+
end
|
81
|
+
|
82
|
+
def desc(desc)
|
83
|
+
@desc = desc
|
84
|
+
end
|
85
|
+
|
86
|
+
def assertions(&block)
|
87
|
+
@assertions = block
|
88
|
+
end
|
89
|
+
|
90
|
+
def rec_symbolize(obj)
|
91
|
+
if obj.class == Hash
|
92
|
+
obj.symbolize_keys!
|
93
|
+
obj.map {|k,v| rec_symbolize(v) }
|
94
|
+
elsif obj.class == Array
|
95
|
+
obj.map {|v| rec_symbolize(v) }
|
96
|
+
end
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class <%= class_name.pluralize %>Test < Flunk
|
4
|
+
|
5
|
+
setup do
|
6
|
+
end
|
7
|
+
|
8
|
+
# Write tests that should succeed to make sure the required functionality works.
|
9
|
+
test "Test Title" do
|
10
|
+
desc "A description of the function this tests"
|
11
|
+
path "resource/:id"
|
12
|
+
method :get
|
13
|
+
username @user.username
|
14
|
+
password @user.password
|
15
|
+
status :ok
|
16
|
+
assertions {
|
17
|
+
assert_equal 2, 2
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Write a test that SHOULD fail to ensure your application handles bad requests gracefully.
|
23
|
+
flunk "Test Title", "Why it flunks" do
|
24
|
+
path "/resource/:id"
|
25
|
+
method :get
|
26
|
+
status :unauthorized
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/test/test_flunk.rb
ADDED
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flunk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Kirk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-01 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A gem for testing a Ruby on Rails web APIs by simulating a client.
|
15
|
+
email: atomkirk@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- flunk-0.0.1.gem
|
24
|
+
- flunk.gemspec
|
25
|
+
- lib/flunk.rb
|
26
|
+
- lib/generators/flunk_test/USAGE
|
27
|
+
- lib/generators/flunk_test/flunk_test_generator.rb
|
28
|
+
- lib/generators/flunk_test/templates/flunk_test.rb
|
29
|
+
- lib/tasks/flunk.rake
|
30
|
+
- test/test_flunk.rb
|
31
|
+
homepage: https://github.com/mysterioustrousers/flunk
|
32
|
+
licenses: []
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.24
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: A gem for testing a Ruby on Rails web APIs by simulating a client.
|
55
|
+
test_files:
|
56
|
+
- test/test_flunk.rb
|