【问题标题】:Mock a model method in Django在 Django 中模拟模型方法
【发布时间】:2013-11-19 19:58:40
【问题描述】:

我刚刚开始使用Mock 来测试 Django 应用程序,而无需接触数据库。我可以在我的测试中成功地模拟我的模型对象,但我对如何处理涉及对象的模型方法有点困惑。

作为一个例子,我的 models.py 中有这个:

class Skill(models.Model):
   title = models.CharField(max_length=255)
   category = models.ForeignKey(
       SkillCategory, default=None, null=True, blank=True
   )

def __unicode__(self):
    return self.title

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    skill = models.ManyToManyField(Skill, default=None)
    avatar = models.URLField(
        max_length=400, default=None, null=True, blank=True
    )

    def __unicode__(self):
        return self.user.username

    def update_skills(self, cleaned_skills):
        for s in cleaned_skills:
            skill, created = Skill.objects.get_or_create(title=s)
            self.skill.add(skill)

到目前为止,在我的重构测试中,我已经这样做了:

class ProfileTestCase(unittest.TestCase):
    def setUp(self):
        # mock user
        self.user = mock.Mock(spec=User)
        self.user.username = "john"
        self.user.email = "johndoe@hotmail.com"
        self.user.password = "pass"
        # mock profile
        self.userprofile = mock.Mock(spec=UserProfile)
        self.userprofile.user = self.user
        # mock skill category
        self.skill_category = mock.Mock(spec=SkillCategory)
        self.skill_category.title = "foo"
        # mock skill
        self.skill = mock.Mock(spec=Skill)
        self.skill.title = "ninja"
        self.skill.category = self.skill_category

    def test_skillcategory_created(self):
        self.assertEqual(self.skill.category.title, 'foo')

    def test_skill_created(self):
        self.assertEqual(self.skill.title, 'ninja')

    ...
    # and so on

在我使用数据库的旧测试中,我有这个来测试我的模型方法:

    ...

    def test_update_skills(self):
        cleaned_skills = ['mysql', 'unix']
        self.userprofile.update_skills(cleaned_skills)
        skills = self.userprofile.skill.values_list('title', flat=True)
        self.assertTrue('mysql' in skills)

我正在为如何重构这一行而摸不着头脑:

self.userprofile.update_skills(cleaned_skills)

利用Mock

【问题讨论】:

    标签: django unit-testing django-models django-testing django-tests


    【解决方案1】:

    尝试使用Mixer:

    from mixer.backend.django import mixer
    
    skill = mixer.blend(Skill, category__title='foo')
    userprofile = mixer.blend(UserProfile, user__username='john')
    

    【讨论】:

      【解决方案2】:
      1. 你的测试看起来很奇怪
      2. 您可以在不创建数据库记录的情况下测试模型方法,只是不要使用 .create() 或 .save():

        def test_my_method(self):
            my_model = MyModel(someattr='someval', ...)
            self.assertEqual(my_model.my_method(), ...)
        
      3. 有时模拟 .save() 方法来禁用在数据库中创建记录和加速测试是一个不错的决定。查看如何在测试中使用模拟的几个示例:http://nanvel.name/2014/04/testing-django-application#patchmock

      【讨论】:

      • 哇,不敢相信我没想到只是不保存对象。这对于测试将date_added 字段设置为auto_now_add=True 的模型非常有用。
      【解决方案3】:

      您可以模拟get_or_create() 方法以及skill 关系,让您的测试在不访问数据库的情况下运行。

      def test_update_skills(self):
          def mock_get_or_create(title):
              return title, True
          mock_skill = set()
          with mock.patch('analytics.models.Skill.objects.get_or_create',
                          mock_get_or_create), \
                  mock.patch.object(UserProfile, 'skill', mock_skill):
              userprofile = UserProfile()
      
              cleaned_skills = ['mysql', 'unix']
              userprofile.update_skills(cleaned_skills)
      
          self.assertEqual({'mysql', 'unix'}, mock_skill)
      

      我正在帮助django_mock_queries project,它有助于在不访问数据库的情况下提供一堆 Django 查询集功能。它使我们的许多测试变得更快。

      【讨论】:

        猜你喜欢
        • 2015-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-31
        • 2018-01-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多