Skip to content

Commit

Permalink
Reorganize tests; update deps
Browse files Browse the repository at this point in the history
  • Loading branch information
sloria committed Oct 1, 2015
1 parent 9471504 commit 55d45ba
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 263 deletions.
4 changes: 2 additions & 2 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
invoke

# testing
pytest>=2.7.3
pytest==2.8.0
tox>=1.5.0
mock

Expand All @@ -15,4 +15,4 @@ flake8==2.4.1

# Soft requirements
flask-sqlalchemy
marshmallow-sqlalchemy>=0.4.0
marshmallow-sqlalchemy>=0.6.0
18 changes: 1 addition & 17 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
import re
import sys
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand


REQUIRES = [
Expand All @@ -11,18 +9,6 @@
'six>=1.9.0',
]

class PyTest(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True

def run_tests(self):
import pytest
errcode = pytest.main(self.test_args)
sys.exit(errcode)


def find_version(fname):
'''Attempts to find the version number in the file names fname.
Raises RuntimeError if not found.
Expand Down Expand Up @@ -76,7 +62,5 @@ def read(fname):
'Programming Language :: Python :: 3.5',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
test_suite='test_flask_marshmallow',
tests_require=['pytest', 'mock'],
cmdclass={'test': PyTest}
test_suite='tests',
)
21 changes: 19 additions & 2 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,25 @@
build_dir = os.path.join(docs_dir, '_build')

@task
def test():
run('python setup.py test')
def test(watch=False, last_failing=False):
"""Run the tests.
Note: --watch requires pytest-xdist to be installed.
"""
import pytest
flake()
args = []
if watch:
args.append('-f')
if last_failing:
args.append('--lf')
retcode = pytest.main(args)
sys.exit(retcode)

@task
def flake():
"""Run flake8 on codebase."""
run('flake8 .', echo=True)

@task
def clean():
Expand Down
Empty file added tests/__init__.py
Empty file.
87 changes: 87 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Pytest fixtures for the test suite."""
from flask import Flask
from flask_marshmallow import Marshmallow
import pytest

_app = Flask(__name__)

class Bunch(dict):
def __init__(self, *args, **kwargs):
super(Bunch, self).__init__(*args, **kwargs)
self.__dict__ = self

# Models

class Author(Bunch):
pass

class Book(Bunch):
pass

@pytest.fixture
def mockauthor():
author = Author(id=123, name='Fred Douglass')
return author

@pytest.fixture
def mockbook(mockauthor):
book = Book(id=42, author=mockauthor, title='Legend of Bagger Vance')
return book

@_app.route('/author/<int:id>')
def author(id):
return 'Steven Pressfield'

@_app.route('/authors/')
def authors():
return 'Steven Pressfield, Chuck Paluhniuk'

@_app.route('/books/')
def books():
return 'Legend of Bagger Vance, Fight Club'

@_app.route('/books/<id>')
def book(id):
return 'Legend of Bagger Vance'

mar = Marshmallow(_app)

@pytest.yield_fixture(scope='function')
def app():

ctx = _app.test_request_context()
ctx.push()

yield _app

ctx.pop()

@pytest.fixture(scope='function')
def ma(app):
return Marshmallow(app)

@pytest.fixture
def schemas(ma):
class AuthorSchema(mar.Schema):
class Meta:
fields = ('id', 'name', 'absolute_url', 'links')

absolute_url = mar.AbsoluteURLFor('author', id='<id>')

links = mar.Hyperlinks({
'self': mar.URLFor('author', id='<id>'),
'collection': mar.URLFor('authors')
})

class BookSchema(mar.Schema):
class Meta:
fields = ('id', 'title', 'author', 'links')

author = mar.Nested(AuthorSchema)

links = mar.Hyperlinks({
'self': mar.URLFor('book', id='<id>'),
'collection': mar.URLFor('books'),
})
# So we can access schemas.AuthorSchema, etc.
return Bunch(**locals())
36 changes: 36 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from flask import Flask, url_for
from werkzeug.wrappers import BaseResponse
from flask_marshmallow import Marshmallow

def test_deferred_initialization():
app = Flask(__name__)
m = Marshmallow()
m.init_app(app)

assert 'flask-marshmallow' in app.extensions

def test_schema(app, schemas, mockauthor):
s = schemas.AuthorSchema()
result = s.dump(mockauthor)
assert result.data['id'] == mockauthor.id
assert result.data['name'] == mockauthor.name
assert result.data['absolute_url'] == url_for('author',
id=mockauthor.id, _external=True)
links = result.data['links']
assert links['self'] == url_for('author', id=mockauthor.id)
assert links['collection'] == url_for('authors')

def test_jsonify(app, schemas, mockauthor):
s = schemas.AuthorSchema()
resp = s.jsonify(mockauthor)
assert isinstance(resp, BaseResponse)
assert resp.content_type == 'application/json'

def test_links_within_nested_object(app, schemas, mockbook):
s = schemas.BookSchema()
result = s.dump(mockbook)
assert result.data['title'] == mockbook.title
author = result.data['author']
assert author['links']['self'] == url_for('author', id=mockbook.author.id)
assert author['links']['collection'] == url_for('authors')
115 changes: 115 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
from flask import url_for
from werkzeug.routing import BuildError
import pytest
from flask_marshmallow.fields import _tpl

@pytest.mark.parametrize('template', [
'<id>',
' <id>',
'<id> ',
'< id>',
'<id >',
'< id >',
])
def test_tpl(template):
assert _tpl(template) == 'id'
assert _tpl(template) == 'id'
assert _tpl(template) == 'id'


def test_url_field(ma, mockauthor):
field = ma.URLFor('author', id='<id>')
result = field.serialize('url', mockauthor)
assert result == url_for('author', id=mockauthor.id)

mockauthor.id = 0
result = field.serialize('url', mockauthor)
assert result == url_for('author', id=0)

def test_url_field_with_invalid_attribute(ma, mockauthor):
field = ma.URLFor('author', id='<not-an-attr>')
with pytest.raises(AttributeError) as excinfo:
field.serialize('url', mockauthor)
expected_msg = '{0!r} is not a valid attribute of {1!r}'.format(
'not-an-attr', mockauthor)
assert expected_msg in str(excinfo)

def test_url_field_deserialization(ma):
field = ma.URLFor('author', id='<not-an-attr>', allow_none=True)
# noop
assert field.deserialize('foo') == 'foo'
assert field.deserialize(None) is None

def test_invalid_endpoint_raises_build_error(ma, mockauthor):
field = ma.URLFor('badendpoint')
with pytest.raises(BuildError):
field.serialize('url', mockauthor)

def test_hyperlinks_field(ma, mockauthor):
field = ma.Hyperlinks({
'self': ma.URLFor('author', id='<id>'),
'collection': ma.URLFor('authors')
})

result = field.serialize('_links', mockauthor)
assert result == {
'self': url_for('author', id=mockauthor.id),
'collection': url_for('authors')
}

def test_hyperlinks_field_recurses(ma, mockauthor):
field = ma.Hyperlinks({
'self': {
'href': ma.URLFor('author', id='<id>'),
'title': 'The author'
},
'collection': {
'href': ma.URLFor('authors'),
'title': 'Authors list'
}
})
result = field.serialize('_links', mockauthor)

assert result == {
'self': {'href': url_for('author', id=mockauthor.id),
'title': 'The author'},
'collection': {'href': url_for('authors'),
'title': 'Authors list'}
}


def test_hyperlinks_field_recurses_into_list(ma, mockauthor):
field = ma.Hyperlinks([
{'rel': 'self', 'href': ma.URLFor('author', id='<id>')},
{'rel': 'collection', 'href': ma.URLFor('authors')}
])
result = field.serialize('_links', mockauthor)

assert result == [
{'rel': 'self', 'href': url_for('author', id=mockauthor.id)},
{'rel': 'collection', 'href': url_for('authors')}
]

def test_hyperlinks_field_deserialization(ma):
field = ma.Hyperlinks({
'href': ma.URLFor('author', id='<id>')
}, allow_none=True)
# noop
assert field.deserialize('/author') == '/author'
assert field.deserialize(None) is None

def test_absolute_url(ma, mockauthor):
field = ma.AbsoluteURLFor('authors')
result = field.serialize('abs_url', mockauthor)
assert result == url_for('authors', _external=True)

def test_absolute_url_deserialization(ma):
field = ma.AbsoluteURLFor('authors', allow_none=True)
assert field.deserialize('foo') == 'foo'
assert field.deserialize(None) is None

def test_aliases(ma):
from flask_marshmallow.fields import UrlFor, AbsoluteUrlFor, URLFor, AbsoluteURLFor
assert UrlFor is URLFor
assert AbsoluteUrlFor is AbsoluteURLFor

0 comments on commit 55d45ba

Please sign in to comment.