factory_boy で遭遇したエラー

テストコードでテストデータを作る時に今までは以下のようにしてた。

from unittest import TestCase
from models.user import User

class TestUser(object):
    def setUp(self):
        self._init()

    def _init(): 
        user = User(name='foo', password='pass')
        db_session.add(user)
        db_session.commit()

    def test_auth_fail(self):
        ret = User.auth(name='bar', passsord='pass')
        self.assertFalse(ret)
from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, Boolean
from werkzeug import generate_password_hash, check_password_hash
from models.db import Base


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    password = Column(String(128))
    delete_flag = Column(Boolean, default=False)
    created_at = Column(DateTime, default=datetime.now)
    updated_at = Column(DateTime, default=datetime.now)
        
    def __init__(self, name, password, delete_flag=False):
        self.name = name
        self.set_password(password)
        self.delete_flag = delete_flag

色々自分のコードを見直している所なので、factory_boy を使ってみたら、エラーが起きた。

import factory
from factory.alchemy import SQLAlchemyModelFactory
from models.db import session
from models.user import User

class UserFactory(SQLAlchemyModelFactory):
    FACTORY_FOR = User
    FACTORY_SESSION = session
        
    id = factory.Sequence(lambda n: n)
    name = factory.Sequence(lambda n: u'User%d' % n)
    password = factory.Sequence(lambda n: u'pass%d' % n)
    delete_flag = False
class TestUser(TestCase):
    def setUp(self):
        self.create_app() 
        UserFactory(name='foo', password='pass')

    def test_auth_fail(self):
        ret = User.auth(name='bar', passsord='pass')
        self.assertFalse(ret)

これでテストコードを実行したら、以下の様なエラーが発生した。

factory/alchemy.py", line 49, in _create
    obj = target_class(*args, **kwargs)
nose.proxy.TypeError: __init__() got an unexpected keyword argument 'id'

User クラスに __init__ でコンストラクタで追加しているのが原因のよう。
__init__() を削るか、以下の様に FACTORY_HIDDEN_ARGS を追加すれば OK

class UserFactory(SQLAlchemyModelFactory):
    FACTORY_FOR = User
    FACTORY_SESSION = session
    FACTORY_HIDDEN_ARGS = ('id',)
        
    id = factory.Sequence(lambda n: n)
    name = factory.Sequence(lambda n: u'User%d' % n)
    password = factory.Sequence(lambda n: u'pass%d' % n)
    delete_flag = False

factory_boy の為に __init__() を削るのは間違ってるので、コンストラクタがある場合は、FACTORY_HIDDEN_ARGS を使うのが良い。