注册账号测试用例怎么写? [英] How to write registering account test cases?

查看:58
本文介绍了注册账号测试用例怎么写?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是关于这篇文章的后续问题

根据原始帖子的建议调整我的代码后,以下是我的完整工作代码.

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

  1. 如何测试 createAccount() 是否可以成功创建帐户或可以抛出异常?

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

def test_canCreateAccount(ctrl):#有效的邮箱和密码电子邮件 = 'hello@gmail.com'密码 1 = '漂亮'密码 2 = '漂亮'帐户 = ctrl.createAccount()断言 account.email == 电子邮件断言 account.password == password1

  1. createAccount() 是否违反了这句话?它没有接受输入的参数.

<块引用>

编写接受输入并返回结果的函数.无副作用.

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

<块引用>

不要在控制流中使用异常.

** 或者我误解了什么?

  1. 无情地削减功能,直到它们只做一件事.

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

  1. 我希望电子邮件输入最多可再次显示 3 次.之后,应用程序引发异常.如何做到这一点以便于测试?

类 CreateAccountFailed(异常):经过类 PassNotValid(CreateAccountFailed):经过类 PassNotMatch(CreateAccountFailed):经过类 EmailNotOK(CreateAccountFailed):经过类RegisterUI:def getEmail(self):return input("请输入您的电子邮件:")def getPassword1(self):return input("请输入密码:")def getPassword2(self):return input("请确认您的密码:")def getSecKey(self):return input("请输入您的安全关键字:")def printMessage(self, message):打印(消息)类注册控制器:def __init__(self, view):self.view = 查看def displaymessage(self, message):self.view.printMessage(消息)def ValidateEmail(self, email):email_obj = 电子邮件(电子邮件)返回 email_obj.isValidEmail() 而不是 accounts.isDuplicate(email)def ValidatePassword(self, password):返回 Password.isValidPassword(password)def CheckPasswordMatch(self, password1, password2):返回 Password.isMatch(password1, password2)def makeAccount(self, email, password, seckey):返回帐户(电子邮件(电子邮件),密码(密码),seckey)def createAccount(self):电子邮件 = self.view.getEmail()如果不是 self.ValidateEmail(email):raise EmailNotOK("重复或不正确的格式")密码 1 = self.view.getPassword1()如果不是 self.ValidatePassword(password1):raise PassNotValid("密码无效")密码 2 = self.view.getPassword2()如果不是 self.CheckPasswordMatch(password1, password2):raise PassNotMatch("密码不匹配")返回 self.makeAccount(email, password1, self.view.getSecKey())def tryCreateAccount(self):尝试:account = self.createAccount()self.displaymessage("账户创建成功")返回帐户除了 CreateAccountFailed 为 e:self.displaymessage(str(e))类寄存器(选项):def执行(自我):视图 = RegisterUI()controller_one = 注册控制器(视图)controller_one.tryCreateAccount()

解决方案

注意:另一个答案中的代码是不是最好的代码,但它比我们开始的地方有了很大的改进.重构的一部分是知道什么时候足够好.请记住,当您阅读本文时,还有更多改进可以进行,但使 createAccount() 可测试的目标已经实现.

<小时><块引用>

  1. 这是我的测试,但 createAccount() 没有参数,那么如何向其中添加输入以进行测试?

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

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

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

from unittest.mock import Mock属性 = {'getEmail.return_value':电子邮件,'getPassword1.return_value': 密码 1,'getPassword2.return_value':密码2,'getSecKey'.return_value': seckey}mock_view = Mock(**属性)

mock_view.getEmail() 将返回 email 等等.将其用作控制器的视图并继续.

ctrl = RegisterController(mock_view)帐户 = ctrl.createAccount()断言 account.email == 电子邮件断言 account.password == password1断言 account.seckey == seckey

或者,您可以编写 RegisterUI 的子类,仅用于测试它在构造函数中获取它的属性并覆盖 getEmail() 和朋友以返回它们.类似于模拟,但更有条理.

<块引用>

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

技术上是的,但这是一个经验法则.您可以传入 view 而不是使用 self.view,但控制器的全部意义在于弥合视图和模型之间的差距.它可以访问 UI 是合适的.

createAccount() 是一个集成函数.它封装了使用来自 UI 的信息创建帐户的过程;无需了解 UI 或帐户的详细信息.这很好.您可以更改帐户创建过程,所有调用 createAccount() 的内容仍然有效.

<块引用>

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

是的,if 是控制流.但是 createAccount() 没有使用控制流的异常.

例外是针对例外的情况.open 打开一个文件.如果它无法打开文件,则会出现异常.createAccount() 创建一个帐户.如果创建异常账户失败,则抛出异常.

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

但是,isEmailValid(email) 可能会使用异常来指明电子邮件无效的原因.例如,它可以抛出 EmailIsDuplicate 以指示重复和 EmailIsInvalid 以指示这是格式问题.

def ValidateEmail(self, email):email_obj = 电子邮件(电子邮件)如果 !accounts.isDuplicate(email):引发 EmailIsDuplicate()如果 !email_obj.isValidEmail():引发 EmailIsInvalid()返回真

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

尝试:self.ValidateEmail(email)除了 EmailIsDuplicateself.displaymessage("那个邮箱已经注册了.")除了 EmailIsInvalidself.displaymessage("邮件格式不正确.")

createAccount() 正在做什么.

<块引用>

  1. [如果我应该无情地削减功能,直到它们做一件事",为什么] createAccount() 做了两件事吗?它从用户输入中获取价值,然后进行验证.

从外部的角度来看,它只做一件事:它处理根据用户输入创建帐户.它究竟是如何做到的,这是故意的一个黑匣子.这个信息隐藏意味着如果创建帐户的细节发生变化,对其余部分的影响程序有限.

如果后来决定某个帐户需要一个名称,您可以将其添加到 createAccount()(和 RegisterUI.getName)中,而无需更改其界面.

<块引用>

  1. 我想[作为有效电子邮件的用户最多 3 次].之后,应用程序引发异常.如何做到这一点以便于测试?

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

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

def AskForValidEmail(self):对于范围内的 x (0, 3):电子邮件 = self.view.getEmail()如果 self.ValidateEmail(email):回邮件别的:self.displaymessage("电子邮件无效或重复,请重试")提高 EmailNotOK

类似地,我们会将询问密码和验证密码合并为一种方法.现在我明白 while 1 的用途了,你要问,直到他们给你一个有效的密码.

def AskForValidPassword(self):而 1:密码 1 = self.view.getPassword1()密码 2 = self.view.getPassowrd2()如果 !Password.isMatch(password1, password2):self.displaymessage("密码不匹配")elif !Password.isValidPassword(密码):self.displaymessage("密码无效")别的返回密码1

然后 createAccount() 调用它们使其更加纤薄.

def createAccount(self):电子邮件 = self.AskForValidEmail()密码 = self.AskForValidPassword()返回 self.makeAccount(email, password1, self.view.getSecKey())

要测试 AskForValidEmail,您可以创建一个更高级的 RegisterUI 模拟.getEmail 不只是返回一个字符串,它可以在前两次调用时返回无效电子邮件,在第三次调用时返回有效电子邮件.

This is a follow up question on this post

After tweak my code as suggestion on the original post, below is my full working code.

However, I have some problems and questions:

  1. How to test createAccount() that can create account successfully or can throw exception ?

This is my test but createAccount() doesn't have parameters, so how to add input to it for testing ?

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. Does createAccount() violate this sentence ? It doesn't have parameters that take input.

Write functions that take input and return a result. No side effects.

  1. "if" statement in createAccount() is control flow ? If yes, whether it violate this sentence ? **

Don't use exceptions for control flow.

** Or I misunderstand about something ?

  1. Ruthlessly shave functions down until they do one thing.

So, why createAccount() do 2 things ? It get value from user input then validate

  1. I want email input will be shown again up to 3 times. After that, app raises exception. How to do that for easy testing ?



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()


解决方案

Note: the code in the other answer is not the best code, but it's a vast improvement over where we started. Part of refactoring is knowing when it's good enough. Keep in mind as you read this, there are more improvements which could be made, but the goal of making createAccount() testable was achieved.


  1. This is my test but createAccount() doesn't have parameters, so how to add input to it for testing?

createAccount gets its information from self.view. That's a RegisterUI object. RegisterUI's methods are interactive which makes them difficult to use in tests.

Fortunately we can pass any view we like to RegisterController. We're not testing RegisterUI, it should have its own tests, just how RegisterController uses RegisterUI. So we'll make a version of RegisterUI just for testing and use that.

We can make a Mock object that responds to RegisterUI's methods.

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() will return the email and so on. Use that as the controller's view and go.

ctrl = RegisterController(mock_view)

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

Alternatively you can write a subclass of RegisterUI just for testing which takes it's attributes in the constructor and overrides getEmail() and friends to return them. Similar to a mock, but a little more organized.

  1. Does createAccount() violate [Write functions that take input and return a result. No side effects.]? It doesn't have parameters that take input.

Technically yes, but that's a rule of thumb. You could pass in the view instead of using self.view, but the whole point of a controller is to bridge the gap between the view and the models. It's appropriate that it would have access to the UI.

createAccount() is an integration function. It encapsulates the process of creating an account using information from the UI; no knowledge of the details of the UI nor the account is required. This is good. You can change the account creation process and everything that calls createAccount() will still work.

  1. "if" statement in createAccount() is control flow? If yes, [is this using exceptions for control flow?]

Yes, an if is control flow. But createAccount() is not using exceptions for control flow.

Exceptions are for exceptional cases. open opens a file. If it fails to open a file you get an exception. createAccount() creates an account. If it fails to create an account that is exceptional, so it throws an exception.

Contrast this with a function like isEmailValid(email). This is asking whether an email is valid or not. Using an exception to indicate an invalid email would be inappropriate; it is totally expected that isEmailValid(email) will be given an invalid email. An invalid email is a normal condition for isEmailValid. Instead it should return a simple boolean.

However, isEmailValid(email) might use exceptions to indicate why the email was invalid. For example, it could throw EmailIsDuplicate to indicate a duplicate and EmailIsInvalid to indicate it's a formatting problem.

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

Then the caller could use the exception to display an appropriate error.

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

Which is what createAccount() is doing.

  1. [If I should "ruthlessly shave functions down until they do one thing", why does] createAccount() do 2 things ? It get value from user input then validates.

From the outside perspective it does one thing: it handles creating an account from user input. Exactly how it does that is deliberately a black box. This information hiding means if the details of how creating an account works changes, the effects on the rest of the program are limited.

If later its decided that an account needs a name, you can add that to createAccount() (and RegisterUI.getName) without changing its interface.

  1. I want to [as the user for a valid email up to 3 times]. After that, app raises exception. How to do that for easy testing?

When I was working on your code yesterday I didn't realize self.view.getEmail() was interactive! That explains the infinite loops. I didn't understand that.

We'd add another method to encapsulate asking for a valid email.

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

Similarly we'd fold asking for the password and verifying it into one method. Now I understand what the while 1 was for, you want to ask until they give you a valid password.

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

And then createAccount() calls them making it even slimmer.

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

To test AskForValidEmail you can make a fancier RegisterUI mock. Instead of getEmail just returning a string, it can return an invalid email on the first two calls and a valid email on the third.

这篇关于注册账号测试用例怎么写?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆