From d1d832ba97285f879c6d8a1740c74625cb2ec45a Mon Sep 17 00:00:00 2001 From: Steven Loria Date: Sat, 7 Dec 2013 17:50:59 -0600 Subject: [PATCH] Add Factory Boy support --- README.rst | 3 ++- {{cookiecutter.repo_name}}/requirements/dev.txt | 1 + .../{{cookiecutter.repo_name}}/app.py | 2 +- .../{{cookiecutter.repo_name}}/settings.py | 1 + .../{{cookiecutter.repo_name}}/tests/factories.py | 15 +++++++++++++++ .../tests/test_models.py | 10 ++++++++++ .../{{cookiecutter.repo_name}}/user/models.py | 12 +++++++----- 7 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/factories.py diff --git a/README.rst b/README.rst index d250497..db2a0f0 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ Features - Flask-WTForms with login and registration forms - Flask-Login for authentication - Procfile for deploying to a PaaS (e.g. Heroku) -- Flask-Testing and nose for testing +- Flask-Testing, nose, and Factory-Boy for testing - A simple ``manage.py`` script. - CSS and JS minification using Flask-Assets - Utilizes best practices: `Blueprints `_ and `Application Factory `_ patterns @@ -69,6 +69,7 @@ Changelog - Use Flask-Bcrypt for password hashing. - Flask-Testing support. - Flask-Login for authentication. +- Use Factory-Boy for test factories. 0.2.0 (09/21/2013) ****************** diff --git a/{{cookiecutter.repo_name}}/requirements/dev.txt b/{{cookiecutter.repo_name}}/requirements/dev.txt index aa257a0..d339fdc 100644 --- a/{{cookiecutter.repo_name}}/requirements/dev.txt +++ b/{{cookiecutter.repo_name}}/requirements/dev.txt @@ -4,6 +4,7 @@ # Testing nose Flask-Testing +factory-boy>=2.2.1 # Management script Flask-Script diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py index 89066d5..751fd9b 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py @@ -3,7 +3,7 @@ from flask import Flask from flask.ext.assets import Environment from webassets.loaders import PythonLoader -from {{ cookiecutter.repo_name }}.settings import ProdConfig +from {{cookiecutter.repo_name}}.settings import ProdConfig from {{cookiecutter.repo_name}}.assets import assets from {{cookiecutter.repo_name}}.extensions import login_manager from {{cookiecutter.repo_name}}.database import db diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py index 4727f8f..df862f2 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py @@ -5,6 +5,7 @@ class Config(object): SECRET_KEY = 'shhhh' APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir)) + BCRYPT_LEVEL = 13 class ProdConfig(Config): ENV = 'prod' diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/factories.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/factories.py new file mode 100644 index 0000000..6c15162 --- /dev/null +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/factories.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from factory import Sequence, PostGenerationMethodCall +from factory.alchemy import SQLAlchemyModelFactory + +from {{cookiecutter.repo_name}}.user.models import User +from {{cookiecutter.repo_name}}.database import db + + +class UserFactory(SQLAlchemyModelFactory): + FACTORY_SESSION = db.session + FACTORY_FOR = User + + username = Sequence(lambda n: "user{0}".format(n)) + email = Sequence(lambda n: "user{0}@example.com".format(n)) + password = PostGenerationMethodCall("set_password", 'example') diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py index f2b8547..ab1decb 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py @@ -6,6 +6,7 @@ from flask.ext.testing import TestCase from {{ cookiecutter.repo_name }}.app import create_app from {{ cookiecutter.repo_name }}.database import db from {{ cookiecutter.repo_name }}.user.models import User +from .factories import UserFactory class TestUser(TestCase): @@ -23,6 +24,15 @@ class TestUser(TestCase): db.session.remove() db.drop_all() + def test_factory(self): + user = UserFactory(password="myprecious") + assert_true(user.username) + assert_true(user.email) + assert_true(user.created_at) + assert_false(user.is_admin) + assert_false(user.active) + assert_true(user.check_password("myprecious")) + def test_check_password(self): user = User(username="foo", email="foo@bar.com", password="foobarbaz123") diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/models.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/models.py index 930200e..c8f73d6 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/models.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/models.py @@ -12,24 +12,26 @@ class User(UserMixin, CRUDMixin, db.Model): __tablename__ = 'users' username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(80), unique=True, nullable=False) - password_hash = db.Column(db.String, nullable=False) + password = db.Column(db.String, nullable=False) # The hashed password created_at = db.Column(db.DateTime(), nullable=False) active = db.Column(db.Boolean()) is_admin = db.Column(db.Boolean()) - def __init__(self, username, email, password, active=False, is_admin=False): + def __init__(self, username=None, email=None, password=None, + active=False, is_admin=False): self.username = username self.email = email - self.set_password(password) + if password: + self.set_password(password) self.active = active self.is_admin = is_admin self.created_at = dt.datetime.utcnow() def set_password(self, password): - self.password_hash = bcrypt.generate_password_hash(password) + self.password = bcrypt.generate_password_hash(password) def check_password(self, password): - return bcrypt.check_password_hash(self.password_hash, password) + return bcrypt.check_password_hash(self.password, password) def __repr__(self): return ''.format(username=self.username)