繁体   English   中英

如何编写注册帐户测试用例?

[英]How to write registering account test cases?

这是此帖子的后续问题

在对我的代码进行了调整之后,下面是我的完整工作代码。

但是,我有一些问题和疑问:

  1. 如何测试可以成功创建帐户或引发异常的createAccount ()?

这是我的测试,但是createAccount()没有参数,因此如何向其添加输入以进行测试?

def test_canCreateAccount(ctrl):
    #valid email and password
    email = 'hello@gmail.com'
    password1 = 'beautiful'
    password2 = 'beautiful'
    account = ctrl.createAccount()
    assert account.email == email
    assert account.password == password1
  1. createAccount()是否违反了这句话? 它没有接受输入的参数。

编写接受输入并返回结果的函数。 没有副作用。

  1. createAccount()中的 “ if”语句是控制流吗? 如果是,是否违反了这句话? **

不要将异常用于控制流。

**还是我误会了某些东西?

  1. 无情地削减职能,直到他们做一件事。

那么,为什么createAccount()做2件事? 它从用户输入中获取价值,然后进行验证

  1. 我希望电子邮件输入最多显示3次。 之后,应用程序引发异常。 如何进行简单测试?


class CreateAccountFailed(Exception):
    pass

class PassNotValid(CreateAccountFailed):
    pass

class PassNotMatch(CreateAccountFailed):
    pass

class EmailNotOK(CreateAccountFailed):
    pass


class RegisterUI:

    def getEmail(self):
        return input("Please type an your email:")

    def getPassword1(self):
        return input("Please type a password:")

    def getPassword2(self):
        return input("Please confirm your password:")

    def getSecKey(self):
        return input("Please type your security keyword:")

    def printMessage(self, message):
        print(message)


class RegisterController:
    def __init__(self, view):
        self.view = view

    def displaymessage(self, message):
        self.view.printMessage(message)

    def ValidateEmail(self, email):
        email_obj = Email(email)
        return email_obj.isValidEmail() and not accounts.isDuplicate(email)

    def ValidatePassword(self, password):
        return Password.isValidPassword(password)

    def CheckPasswordMatch(self, password1, password2):
        return Password.isMatch(password1, password2)

    def makeAccount(self, email, password, seckey):
        return Account(Email(email), Password(password), seckey)

    def createAccount(self):
        email = self.view.getEmail()
        if not self.ValidateEmail(email):
            raise EmailNotOK("Duplicate or incorrect format")

        password1 = self.view.getPassword1()
        if not self.ValidatePassword(password1):
            raise PassNotValid("Password is not valid")

        password2 = self.view.getPassword2()
        if not self.CheckPasswordMatch(password1, password2):
            raise PassNotMatch("Passwords don't match")

        return self.makeAccount(email, password1, self.view.getSecKey())

    def tryCreateAccount(self):
        try:
            account = self.createAccount()
            self.displaymessage("Account was created successfully")
            return account
        except CreateAccountFailed as e:
            self.displaymessage(str(e))

class Register(Option):
    def execute(self):
        view = RegisterUI()
        controller_one = RegisterController(view)
        controller_one.tryCreateAccount()


注意: 另一个答案中的代码不是最好的代码,但是它对我们从何处开始进行了巨大的改进。 重构的一部分是知道什么时候足够好。 请记住,阅读本文时,可以进行更多的改进,但是可以实现createAccount()可测试的目标。


  1. 这是我的测试,但是createAccount()没有参数,那么如何为测试添加输入呢?

createAccountself.view获取其信息。 那是一个RegisterUI对象。 RegisterUI的方法是交互式的,这使得它们很难在测试中使用。

幸运的是,我们可以将我们喜欢的任何视图传递给RegisterController 我们不测试RegisterUI ,它应该具有自己的测试,就像RegisterController如何使用RegisterUI 因此,我们将创建一个RegisterUI版本,仅用于测试和使用。

我们可以制作一个响应RegisterUI方法的Mock对象

from unittest.mock import Mock
attrs = {
  'getEmail.return_value': email,
  'getPassword1.return_value': password1,
  'getPassword2.return_value': password2,
  'getSecKey'.return_value': seckey
}
mock_view = Mock(**attrs)

mock_view.getEmail()将返回email ,依此类推。 使用它作为控制器的视图。

ctrl = RegisterController(mock_view)

account = ctrl.createAccount()
assert account.email == email
assert account.password == password1
assert account.seckey == seckey

另外,您可以编写RegisterUI的子类以进行测试,该子类在构造函数中使用其属性,并覆盖getEmail()和friends以返回它们。 与模拟游戏类似,但更有条理。

  1. createAccount()是否违反[编写接受输入并返回结果的函数。 没有副作用。]? 它没有接受输入的参数。

从技术上说是的,但这是一个经验法则。 您可以传递view而不是使用self.view ,但是控制器的重点是弥合视图和模型之间的差距。 可以访问UI是适当的。

createAccount()是一个集成函数。 它封装了使用UI中的信息创建帐户的过程; 无需了解用户界面的详细信息,也无需帐户。 很好 您可以更改帐户创建过程,所有调用createAccount()仍然可以使用。

  1. createAccount()中的“ if”语句是控制流吗? 如果是,[这是否在控制流中使用异常?]

是的, if是控制流程。 但是createAccount()并未将异常用于控制流。

例外是例外情况。 open打开一个文件。 如果无法打开文件,则会出现异常。 createAccount()创建一个帐户。 如果无法创建例外帐户,则抛出异常。

将此与isEmailValid(email)类的函数进行对比。 这是在询问电子邮件是否有效。 使用异常指示无效的电子邮件是不合适的; 完全预期isEmailValid(email)将收到无效的电子邮件。 无效的电子邮件是isEmailValid的正常情况。 相反,它应该返回一个简单的布尔值。

但是, isEmailValid(email)可能使用异常来指示为什么电子邮件无效。 例如,它可能抛出EmailIsDuplicate表示重复,而EmailIsInvalid表示这是格式问题。

def ValidateEmail(self, email):
    email_obj = Email(email)
    if !accounts.isDuplicate(email):
        raise EmailIsDuplicate()
    if !email_obj.isValidEmail():
        raise EmailIsInvalid()
    return true

然后,调用者可以使用异常来显示适当的错误。

try:
    self.ValidateEmail(email)
except EmailIsDuplicate
    self.displaymessage("That email is already registered.")
except EmailIsInvalid
    self.displaymessage("The email is not formatted correctly.")

createAccount()在做什么。

  1. [如果我应该“无情地削减函数,直到它们做一件事”,为什么] createAccount()做2件事? 它从用户输入中获取价值,然后进行验证。

从外部的角度来看,它做一件事:它负责根据用户输入创建帐户。 确切的说,这是一个黑匣子。 信息隐藏意味着,如果创建帐户的工作方式的详细信息发生更改,则对程序其余部分的影响将受到限制。

如果稍后它决定一个帐户需要一个名称,则可以将该名称添加到createAccount() (和RegisterUI.getName )中,而无需更改其接口。

  1. 我想[作为有效电子邮件的用户最多3次]。 之后,应用程序引发异常。 如何进行简单测试?

昨天在处理您的代码时,我没有意识到self.view.getEmail()是交互式的! 那解释了无限循环。 我不明白。

我们将添加另一种方法来封装请求有效的电子邮件。

def AskForValidEmail(self):
    for x in range(0, 3):
        email = self.view.getEmail()
        if self.ValidateEmail(email):
            return email
        else:
            self.displaymessage("Email was invalid or a duplicate, please try again")
    raise EmailNotOK

同样,我们将折叠询问密码并将其验证为一种方法。 现在,我了解了while 1用途是什么,您想问一下,直到他们给您提供有效的密码。

def AskForValidPassword(self):
    while 1:
        password1 = self.view.getPassword1()
        password2 = self.view.getPassowrd2()
        if !Password.isMatch(password1, password2):
            self.displaymessage("The passwords do not match")
        elif !Password.isValidPassword(password):
            self.displaymessage("The password is invalid")
        else
            return password1

然后createAccount()调用它们,使其更纤薄。

def createAccount(self):
    email = self.AskForValidEmail()
    password = self.AskForValidPassword()
    return self.makeAccount(email, password1, self.view.getSecKey())

要测试AskForValidEmail您可以制作一个更好的RegisterUI模拟。 代替getEmail仅返回字符串,它可以在前两个调用中返回无效的电子邮件,而在第三个调用中返回有效的电子邮件。

这是上述Schwern答案的补充(添加更多信息)。 我们需要确定测试的目的。 我认为以下两个原因,每个原因导致使用相同策略实现模拟。

  1. 为了验证用户输入正确的电子邮件3次后是否抛出异常。
  2. 为了验证两次无效时间后,用户在第三次输入有效的电子邮件。

该策略是要有一个全局数组(如果有要模拟的对象,请改用对象的属性)以跟踪调用模拟的次数。 以下是建议。

count_try = [
    'mock_3_failed': 0,
    'mock_3rd_good': 0,
    ]

def mock_3_failed():
    values = ['1st', '2nd', '3rd']
    current_count = count_try['mock_3_failed']
    result = values[current_count]
    # When count reaches len(values) - 1 (2 for 3 element list), reset to 0
    count_try['mock_3_failed'] = (current_count + 1
            ) if current_count < len(values) - 1 else 0
    return result

def mock_3rd_good():
    values = ['1st', '2nd', 'third@company.com']
    current_count = count_try['mock_3rd_good']
    result = values[current_count]
    count_try['mock_3_failed'] = (current_count + 1
            ) if current_count < len(values) - 1 else 0
    return result

之后,您可以拥有2个测试功能。 一个使用了mock_3_failed,然后断言抛出了异常。 另一个使用mock_3rd_good然后断言返回了预期的结果。

另一个补充是重构“提升/尝试”控制流。 当前,我们将逻辑知识存储在两个位置:ValidateEmail函数用于检查,AskForValidEmail用于报告错误。 相反,我们只能将其重构到一个位置:ValidateEmail函数。 这将有助于将来的代码更改。

def ValidateEmail(self, email):
    email_obj = Email(email)
    if !accounts.isDuplicate(email):
        raise EmailNotOK("That email is already registered.")
    if !email_obj.isValidEmail():
        raise EmailNotOK("The email is not formatted correctly.")
    return true

def AskForValidEmail(self):
    MAX_TRY = 3
    for x in range(0, MAX_TRY):
        email = self.view.getEmail()
        try:
            self.ValidateEmail(email)
        except EmailNotOK as e:
            self.displaymessage(str(e))
    raise EmailNotOK('Reached max number of trying (%d).')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM