模拟Django用户的最佳方法是什么?

时间:2019-10-06 05:03:00

标签: django python-3.x

我正在尝试通过Mock实例化 User 实例。该模拟实例将被传递到另一个模型 Profile ,在该模型中,当调用清洗方法时,我正在检查是否有任何验证错误。

但是我得到了:AttributeError: Mock object has no attribute '_state'

上一篇文章:How to mock users and requests in django。不过,我想避免任何数据库调用。

在这种情况下,模拟可以做什么?

#models.py

class Profile(models.Model):

    hobby = "Hobbyist"
    develop = "Developer"
    coding_level = (
        (hobby, hobby),
        (develop, develop)
    )

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    birth = models.DateField(verbose_name="Date Of Birth")
    coding_level = models.CharField(
        verbose_name="Experience",
        max_length=20,
        choices=coding_level, default=hobby, blank=False
    )
    bio = models.TextField(
        verbose_name="User Bio",
        validators=[MinValueValidator(10)]
    )
    github = models.URLField(
        verbose_name="GitHub link",
        validators=[check_submitted_link],
        unique=True
    )
    avatar = models.ImageField(upload_to="images/%Y/%m/%d/")
#test_models.py

class TestProfile__001(SimpleTestCase):

    def setUp(self):
        self.test_user = Mock(
            spec=User,
            username="test_username",
            email="test@email.com"
        )

        self.profile_data = {
            'user': self.test_user,
            'birth': '2019-10-07',
            'coding_level': 'hobbyist',
            'bio': "",
            'github': "http://www.test.com",
            'avatar': "image.txt"
        }

    def test_create_profile_fail(self):
        with self.assertRaises(ValidationError):
            test_profile = Profile(**self.profile_data)
            test_profile.clean_fields()

1 个答案:

答案 0 :(得分:0)

我认为测试clean_fields没有意义,因为它是经过充分测试的Django代码库的already a part。但是,如果您坚持要进行测试,则绝对不应嘲笑User

让我们看看您要在此处测试的代码(这是clean_fields的摘录):

        raw_value = getattr(self, f.attname)
        if f.blank and raw_value in f.empty_values:
            continue
        try:
            setattr(self, f.attname, f.clean(raw_value, self))
        except ValidationError as e:
            errors[f.name] = e.error_list

我们看到它遍历模型的每个字段,试图调用其clean方法(source):

def clean(self, value):
    """
    Validate the given value and return its "cleaned" value as an
    appropriate Python object. Raise ValidationError for any errors.
    """
    value = self.to_python(value)
    self.validate(value)
    self.run_validators(value) 
    return value

OneToOneField本身不引入任何这些方法,它在ForeignKey类的类层次结构中做得更高。这是validate方法的the main part

    using = router.db_for_read(self.remote_field.model, instance=model_instance)
    qs = self.remote_field.model._default_manager.using(using).filter(
        **{self.remote_field.field_name: value}
    )
    qs = qs.complex_filter(self.get_limit_choices_to())
    if not qs.exists():
        raise exceptions.ValidationError(
            self.error_messages['invalid'],
            code='invalid',
            params={
                'model': self.remote_field.model._meta.verbose_name, 'pk': value,
                'field': self.remote_field.field_name, 'value': value,
            },  # 'pk' is included for backwards compatibility
        )

如您所见,整个验证只包含带有数据库调用的查询构造!因此,如果您尝试避免访问数据库,则应该完全跳过所有外键的验证:

def test_create_profile_fail(self):
    with self.assertRaises(ValidationError):
        test_profile = Profile(**self.profile_data)
        test_profile.clean_fields(exclude=['user'])  # don't forget to explain your decision in the comments

总而言之,模拟用户的最佳方法是在数据库中实际创建它。如果您想避免样板,可以使用factory_boy package