>>> data = [('oauth_consumer_key', 'the-PFJ'),
('oauth_signature_method', 'HMAC-SHA1'),
('oauth_timestamp', 1323493200),
('oauth_nonce', 'thwow-him-to-the-floor')]
>>> qs = urlencode(sorted(data))
>>> url = quote('http://example.com/pilate/', safe='')
>>> base = 'GET&{url}&{qs}'.format(url=url, qs=qs)
>>> key = '{consumer}&'.format(consumer='secret')
>>> signature = hmac.new(key, base, sha1).digest()
>>> signature = quote(b64encode(signature), safe='')
>>> # 'N9l2SigcJUjORYMn9vGgJyjGP%2FI%3D'
Django default:
# tests.py
If we're lucky:
# test_views.py
class OAuthSignatureTestCase(TestCase):
# ...
def setUp(self):
self.user = create_test_api_user()
self.url = 'http://example.com/pilate/'
self.method = 'GET'
def test_verify(self):
# ... all ``verify`` tests go here.
def test_verify(self):
oauth = dict(oauth_consumer_key=self.user.key,
oauth_signature_method=self.user.method,
oauth_timestamp=1323493200,
oauth_nonce='thwow-him-to-the-floor')
# The happy path -- a valid signature
request = dict(oauth)
signature = sign(request, self.url, self.method,
self.user.secret)
is_valid = verify(signature, request)
self.assertTrue(is_valid)
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old
request = dict(oauth, oauth_timestamp=100)
signature = sign(request, self.url, self.method,
self.user.secret)
is_valid = verify(signature, request)
self.assertFalse(is_valid)
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists
request = dict(oauth)
save_nonce(timestamp=request['oauth_timestamp'],
nonce=request['oauth_nonce'])
signature = sign(request, self.url, self.method,
self.user.secret)
is_valid = verify(signature, request)
self.assertFalse(is_valid)
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request
signature = sign(oauth, self.url, self.method,
self.user.secret)
request = dict(oauth, oauth_nonce='the-PFJ')
is_valid = verify(signature, request)
self.assertFalse(is_valid)
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request ... elided
# Empty signature
signature = ''
request = dict(oauth)
is_valid = verify(signature, request)
self.assertFalse(is_valid)
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request ... elided
# Empty signature ... elided
# Extra param in request
signature = sign(oauth, self.url, self.method,
self.user.secret)
request = dict(oauth, extra='Manacles!')
self.assertFalse(verify(signature, request))
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request ... elided
# Empty signature ... elided
# Extra param in request ... elided
# Missing nonce ...
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request ... elided
# Empty signature ... elided
# Extra param in request ... elided
# Missing nonce ... elided
# Nonce exists for another client ...
def test_verify(self):
# oauth dict ... elided
# The happy path ... elided
# Timestamp too old ... elided
# Nonce already exists ... elided
# Incorrect nonce in request ... elided
# Empty signature ... elided
# Extra param in request ... elided
# Missing nonce ... elided
# Nonce exists for another client ... elided
# and so on ...
=====================================================
FAIL: test_verify (test_views.OAuthSignatureTestCase)
-----------------------------------------------------
Traceback (most recent call last):
File "tests/test_views.py", line 42, in test_verify
self.assertFalse(is_valid)
AssertionError
-----------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
=====================================================
FAIL: test_verify (test_views.OAuthSignatureTestCase)
-----------------------------------------------------
File "tests/test_views.py", line 42, in test_verify
self.assertFalse(is_valid)
AssertionError
request = dict(oauth)
signature = sign(request, self.url, self.method,
self.user.secret)
is_valid = verify(signature, request)
Repeated over and over
def setUp(self):
self.user = create_test_api_user()
self.url = 'http://example.com/pilate/'
self.method = 'GET'
What is boilerplate? What is relevant?
When a failing test makes us read 20+ lines of test code, we die inside.
class MyTestCase(TestCase):
def setUp(self):
# Arrange generic
def test_a_method(self):
# Arrange specific
# Act
# Assert
# repeat ...
Why?
User stories (specifications):
As a <role>, I want <feature>, so that <benefit>.
break down into scenarios (acceptance criteria):
Given <context>, when <event>, then <outcome>.
Want
class When___(TestCase):
given = "People's Front of Judea"
@property
def given2(self):
return "blessed-are-the-cheesemakers"
def setUp(self):
# Do "When" using "Given"s
def test_it___(self): # "Then"
expected = "Thwow him to the floor again, sir?"
self.assertEqual(self.result, expected)
class OAuthSignatureFixture(TestCase):
signing_timestamp = 1000
signing_nonce = 'blessed-are-the-cheesemakers'
signing_key = 'the-PFJ'
current_timestamp = 1000
request_nonce = 'blessed-are-the-cheesemakers'
extra = dict()
@property
def data(self):
return dict(oauth_consumer_key=self.signing_key,
# ...
class OAuthSignatureFixture(TestCase):
# ... default "Given"s elided
def save_nonce(self):
"""Override in a subclass to specialize"""
pass
def setUp(self):
"""Do "When" using "Given"s."""
self.user = create_user()
self.save_nonce()
self.is_valid = verify(self.signature,
self.request)
class WhenSignatureIsValid(OAuthSignatureFixture):
def test_it_verifies(self):
self.assertTrue(self.is_valid)
class WhenTimestampIsTooOld(OAuthSignatureFixture):
signature_timestamp = 10
def test_it_does_not_verify(self):
self.assertFalse(self.is_valid)
class WhenNonceAlreadyExists(OAuthSignatureFixture):
def save_nonce(self):
# Create nonce record w/ `self.signing_nonce`
# ...
def test_it_does_not_verify(self):
self.assertFalse(self.is_valid)
class WhenRequestNonceDiffersFromSigningNonce(OAuthS#...
signing_nonce = 'blessed-are-the-cheesemakers'
request_nonce = 'the-PFJ'
def test_it_does_not_verify(self):
self.assertFalse(self.is_valid)
And so on...
======================================================
FAIL: test_it_does_not_verify (test_signature.
WhenNonceAlreadyExists)
------------------------------------------------------
Traceback (most recent call last):
File "tests/oauth/test_signature.py", line 42, in
test_it_does_not_verify
self.assertFalse(self.is_valid)
AssertionError
------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
======================================================
FAIL: test_it_does_not_verify (test_signature.
WhenNonceAlreadyExists)
------------------------------------------------------
We may not even need to read the test's source.
class WhenRequestIsMissingNonce(OAuthSignatureFixture):
request_nonce = ''
def test_it_does_not_verify(self):
self.assertFalse(self.is_valid)
class WhenSignatureIsEmpty(OAuthSignatureFixture):
signature = ''
def test_it_does_not_verify(self):
self.assertFalse(self.is_valid)
I haven't yet maintained these long-term.
Leaves you wanting more (e.g. parameterized tests).