Harper Maddox
CTO of EdgeTheory
Manual Tests
Automated Tests
Tons of administrative work
You can run your whole test suite, a test file, or a specific test case in a file.
assert # is it truthy?
assert_nil # is it nil?
assert_equal # do the 2 parameters match?
assert_match # test string against a pattern
We use them to verify correctness in a test case
It is normal to have multiple assertions in a test case
@user = User.new
@user.name = 'Bruce Springsteen'
@user.title = 'The Boss'
@user.state = 'New Jersey'
@user.save
assert 'Bruce Springsteen', @user.name # pass
assert 'The Boss', @user.title # pass
assert 'Hawaii', @user.state # FAIL
Great for pure functions
Overkill for business logic
Easier to test your routes than test the details
We write tools for social media management. We have to validate the length of text before our users send/schedule a message.
We run our own custom url shortener that generates a new hash for each domain. A common function is to convert an integer (database table ID) into a base 62 string and vice-versa.
require './test/test_helper'
class BaseConversionEncodingTest < MiniTest::Test
def setup
@logger = Logger.new(STDOUT)
end
def test_convert_b10_to_b62_zero
assert_equal('0', Leadify::LinkShortener::Math.to_base(0, base=62))
end
def test_convert_b10_to_b62_one_digit
assert_equal('5', Leadify::LinkShortener::Math.to_base(5, base=62))
end
def test_convert_b10_to_b62_two_digit
assert_equal('A', Leadify::LinkShortener::Math.to_base(36, base=62))
end
def test_convert_b10_to_b62_two_digit_b62
assert_equal('10', Leadify::LinkShortener::Math.to_base(62, base=62))
end
# ...
end
Action | HTTP Method |
---|---|
Create | POST '/resources' |
Read One | GET '/resources/:id' |
Read Many | GET '/resources' |
Update | PUT / POST / PATCH '/resources/:id' |
Delete | DELETE '/resources/:id' |
These are the most common routes in a REST API
They heavily use a permanent data store (database)
All of our routes except deletes return JSON data
And it includes a simple syntax for calling them:
<http method> <route name>, <parameters>, <headers>
# Examples
get '/messages'
post '/messages', text: "Gotta catch 'em all"
post '/shares', { facebook_post_id: '1337' },
'rack.session' => { visit_id: 1 }
def test_post_messages_with_message
post '/messages', text: '@ash_ketchum_all, check out this pikachu
I found digging through the dumpster behind the #Pokestop!'
assert last_response.ok?
response = Oj.load(last_response.body)
assert(response.key?('message'))
message = response['message']
assert_equal '@ash_ketchum_all, check out this pikachu I found
digging through the dumpster behind the #Pokestop!', message['text']
end
My test cases fall into four different patterns:
Since we’re relying on a few external dependencies (Stripe and Twitter), they aren't accessible by our API in test mode. Furthermore, would we want to?
Stripe::Plan.stubs(:create).returns(
stripe_uuid: 'vindaloo',
name: 'Red Dwarf',
subtitle: 'Spaceship',
statement_descriptor: 'EDGETHEORY SB VIP',
amount: 9999,
currency: 'usd',
interval: 'month',
interval_count: 1,
is_active: false
)
Here's a stub overriding a class method, so we don't have to call the Stripe API:
You’ve got to first create a mock object, then you have to extend that mock object with a stub.
# Mock object needed to stub an instance method
object = mock('object')
Stripe::Plan.stubs(:retrieve).returns(object)
object.stubs(:delete).returns(true)
We run these two code snippets in the setup block of our test, so that when we create or delete a plan we can get a predictable result.
We always return the same hash when we create a plan and we can cleanly delete one as well.
By Harper Maddox
Integration Testing in Ruby's Sinatra Framework