pytest:如何避免在自定义用户管理器测试中重复
pytest: how to avoid repetition in custom User manager testing
我正在使用 pytest
和 factory_boy
测试自定义用户管理器。我想测试那些创建新用户所需的信息不完整的情况,但我有不同的必需参数,目前有 3 个(email, username, identification_number)
,但将来可能会有更多。
经理
class UserManager(BaseUserManager):
""" Define a manager for custom User model. """
def create_user(
self,
email: str,
username: str,
identification_number: str,
password: Optional[str] = None,
is_active: bool = True,
is_staff: bool = False,
is_admin: bool = False,
) -> User:
""" Creates and saves a User. """
if not email:
raise ValueError(_("Users must have an email address."))
if not username:
raise ValueError(_("Users must have a username."))
if not identification_number:
raise ValueError(_("Users must have an identification number."))
user = self.model(email=self.normalize_email(email))
user.set_password(password)
user.username = username
user.identification_number = identification_number
user.active = is_active
user.staff = is_staff
user.admin = is_admin
user.save(using=self._db)
return user
测试
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
def test_user_with_no_email(self):
proto_user = UserFactory.build() # User created with factory boy
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
username=proto_user.username,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email="",
username=proto_user.username,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
def test_user_with_no_username(self):
proto_user = UserFactory.build()
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
email=proto_user.email,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email=proto_user.email,
username="",
identification_number=proto_user.identification_number,
password=proto_user._password,
)
def test_user_with_no_identification_number(self):
proto_user = UserFactory.build()
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
email=proto_user.email,
username=proto_user.username,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email=proto_user.email,
username=proto_user.username,
identification_number="",
password=proto_user._password,
)
问题
有很多重复的代码,并且由于所需的参数数量可能会增加,我应该一遍又一遍地对那些额外的参数重复相同的测试。
您可以使用辅助方法和 fixture
来减少重复代码。以下代码块是这种方法的示例。
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
@pytest.fixture
def proto_user(self):
return UserFactory.build()
def helper(self, username, password, email, identification_number):
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
params = {'password': password}
if username:
params['username'] = username
if email:
params['email'] = email
if identification_number:
params['identification_number'] = identification_number
with pytest.raises(TypeError):
User.objects.create_user(**params)
with pytest.raises(ValueError):
User.objects.create_user(
email=email,
username=username,
identification_number=identification_number,
password=password,
)
def test_user_with_no_email(self, proto_user):
self.helper(proto_user.username, proto_user._password, "", proto_user.identification_number)
def test_user_with_no_username(self, proto_user):
self.helper("", proto_user._password, proto_user.email, proto_user.identification_number)
def test_user_with_no_identification_number(self, proto_user):
self.helper(proto_user.username, proto_user._password, proto_user.email, "")
或者你也可以写parametrize
测试。像下面这样,
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
def helper(self, username, password, email, identification_number):
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
params = {'password': password}
if username:
params['username'] = username
if email:
params['email'] = email
if identification_number:
params['identification_number'] = identification_number
with pytest.raises(TypeError):
User.objects.create_user(**params)
with pytest.raises(ValueError):
User.objects.create_user(
email=email,
username=username,
identification_number=identification_number,
password=password,
)
@pytest.mark.parametrize("username,password,email,identification_number", [
("username", "password", "", "identification_number"),
("", "password", "email@email.com", "identification_number"),
("username", "password", "email@email.com", ""),
])
def test_user(self, username, password, email, identification_number):
self.helper(username, password, email, identification_number)
注意:我没有机会 运行 代码。所以你可能需要修改它们。
我正在使用 pytest
和 factory_boy
测试自定义用户管理器。我想测试那些创建新用户所需的信息不完整的情况,但我有不同的必需参数,目前有 3 个(email, username, identification_number)
,但将来可能会有更多。
经理
class UserManager(BaseUserManager):
""" Define a manager for custom User model. """
def create_user(
self,
email: str,
username: str,
identification_number: str,
password: Optional[str] = None,
is_active: bool = True,
is_staff: bool = False,
is_admin: bool = False,
) -> User:
""" Creates and saves a User. """
if not email:
raise ValueError(_("Users must have an email address."))
if not username:
raise ValueError(_("Users must have a username."))
if not identification_number:
raise ValueError(_("Users must have an identification number."))
user = self.model(email=self.normalize_email(email))
user.set_password(password)
user.username = username
user.identification_number = identification_number
user.active = is_active
user.staff = is_staff
user.admin = is_admin
user.save(using=self._db)
return user
测试
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
def test_user_with_no_email(self):
proto_user = UserFactory.build() # User created with factory boy
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
username=proto_user.username,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email="",
username=proto_user.username,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
def test_user_with_no_username(self):
proto_user = UserFactory.build()
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
email=proto_user.email,
identification_number=proto_user.identification_number,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email=proto_user.email,
username="",
identification_number=proto_user.identification_number,
password=proto_user._password,
)
def test_user_with_no_identification_number(self):
proto_user = UserFactory.build()
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
with pytest.raises(TypeError):
User.objects.create_user(
email=proto_user.email,
username=proto_user.username,
password=proto_user._password,
)
with pytest.raises(ValueError):
User.objects.create_user(
email=proto_user.email,
username=proto_user.username,
identification_number="",
password=proto_user._password,
)
问题
有很多重复的代码,并且由于所需的参数数量可能会增加,我应该一遍又一遍地对那些额外的参数重复相同的测试。
您可以使用辅助方法和 fixture
来减少重复代码。以下代码块是这种方法的示例。
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
@pytest.fixture
def proto_user(self):
return UserFactory.build()
def helper(self, username, password, email, identification_number):
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
params = {'password': password}
if username:
params['username'] = username
if email:
params['email'] = email
if identification_number:
params['identification_number'] = identification_number
with pytest.raises(TypeError):
User.objects.create_user(**params)
with pytest.raises(ValueError):
User.objects.create_user(
email=email,
username=username,
identification_number=identification_number,
password=password,
)
def test_user_with_no_email(self, proto_user):
self.helper(proto_user.username, proto_user._password, "", proto_user.identification_number)
def test_user_with_no_username(self, proto_user):
self.helper("", proto_user._password, proto_user.email, proto_user.identification_number)
def test_user_with_no_identification_number(self, proto_user):
self.helper(proto_user.username, proto_user._password, proto_user.email, "")
或者你也可以写parametrize
测试。像下面这样,
import pytest
from django.contrib.auth import get_user_model
from my_app.users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestsUsersManagers:
def helper(self, username, password, email, identification_number):
User = get_user_model()
with pytest.raises(TypeError):
User.objects.create_user()
params = {'password': password}
if username:
params['username'] = username
if email:
params['email'] = email
if identification_number:
params['identification_number'] = identification_number
with pytest.raises(TypeError):
User.objects.create_user(**params)
with pytest.raises(ValueError):
User.objects.create_user(
email=email,
username=username,
identification_number=identification_number,
password=password,
)
@pytest.mark.parametrize("username,password,email,identification_number", [
("username", "password", "", "identification_number"),
("", "password", "email@email.com", "identification_number"),
("username", "password", "email@email.com", ""),
])
def test_user(self, username, password, email, identification_number):
self.helper(username, password, email, identification_number)
注意:我没有机会 运行 代码。所以你可能需要修改它们。