我正在尝试通过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()
答案 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。