工厂男孩与自定义对象

时间:2013-10-30 14:23:11

标签: python django tdd django-testing factory-boy

问题:

在以下情况下使用Factory Boy有什么好处?我真的不明白为什么我不应该只提供自己的自定义对象。如果我错了,请告诉我原因。

我正在使用Factory Boy在我的测试期间创建用户实例,这会动态创建一个UserProfile对象(来自Factory_Boy documentation的标准配方)。

Data类创建将在帖子中传递给表单的数据(我正在使用的其他方法为self.client.post方法提供登录,注册和激活用户的数据。除非我遗漏了某些内容,否则我我必须为每种情况构建一个单独的DjangoModelFactory子类,以便使用ClassName.attributes(),其中数据要求不同。我朝这个方向前进的另一个原因是UserProfile有一个User外键,所以我无法直接调用UserProfileFactory.attributes(),只调用UserFactory.attributes()。为什么不像我正在做的那样自己做?

#Factories.py

IMAGE_PATH = os.path.join(os.path.dirname(__file__), 
                          '../../test_files/test_images/image.jpeg')
class UserProfileFactory(DjangoModelFactory):
    FACTORY_FOR = UserProfile

    user = factory.SubFactory('portal.factories.UserFactory', profile=None)

    first_name = factory.Sequence(lambda n: "Joe_%d" % n)
    last_name = factory.Sequence(lambda n: "Schmoe_%d" % n)
    nickname = factory.Sequence(lambda n: "JoeBlow_%d" % n)
    profile_image = factory.LazyAttribute(lambda t: File(open(IMAGE_PATH)))


class UserFactory(DjangoModelFactory):
    FACTORY_FOR = User

    username = factory.Sequence(lambda n: "user_%d" % n)
    password = make_password("password")
    email = factory.Sequence(lambda n: "user_%d@gmail.com" % n)

    profile = factory.RelatedFactory(UserProfileFactory, 'user')

    @classmethod
    def _generate(cls, create, attrs):
        models.signals.post_save.disconnect(user_post_save, sender=User)
        user = super(UserFactory, cls)._generate(create, attrs)
        models.signals.post_save.connect(user_post_save, sender=User)
        return user


class Data(object):
    def __init__(self):
        self.IMAGE_PATH = os.path.join(os.path.dirname(__file__), 
                                       '../../test_files/test_images/image.jpeg')
        self.profile_image = File(open(IMAGE_PATH))

    def get_profile_update(self, user):
        return {'first_name': 'Jeff',
                'last_name': 'Lebowski',
                'nickname': 'The Dude',
                'profile_image': self.profile_image,
                'user': user.pk,}

    def and_so_on(self):
        continues...

然后我在集成测试期间在以下上下文中使用这样的数据:

class PortalTestCase(TestCase):
    """Shortened and simplified"""
    def test_edit_profile_post(self):
        user = UserFactory.create()
        login_bool = self.client.login(username=user.username,
                                       password=self.data.get_password())
        data = self.data.get_profile_update(user)
        response = self.client.post(reverse(self.get_edit_profile()),
                                data=data, 
                                follow=True)
        success_url = 'http://testserver%s' % reverse(self.get_portal())
        template_name = self.get_portal_template()
        content_text_img = 'src="/' + user.get_profile().profile_image.url + '"'
        self.assertRedirects(response, success_url)
        self.assertTemplateUsed(response, template_name)
        self.assertContains(response, content_text_img)

1 个答案:

答案 0 :(得分:0)

你是对的,在这个单一实例测试案例中,工厂男孩可能有点矫枉过正。但是考虑整个应用程序。通常,您需要使用另一个UserFactory对象。猜猜已经做了什么步法。我选择使用工厂男孩的原因是一致的可重用性。

正如documentation所述,但并未准确说明此工具是为了与您的应用程序一起成长而不是替换单个独立的测试用例。