这是关于 this post 的后续问题
在根据原始帖子的建议调整我的代码后,以下是我的完整工作代码。
但是,我有一些问题和疑问:
这是我的测试,但 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
Write functions that take input and return a result. No side effects.
如果是,是否违反了这句话?
**
Don't use exceptions for control flow.
** 或者我对某事有误解?
Ruthlessly shave functions down until they do one thing.
那么,为什么 createAccount() 做两件事呢?它从用户输入中获取值(value)然后验证
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()
最佳答案
注:the code in the other answer不是最好的代码,但它比我们开始的地方有了很大的改进。重构的一部分是知道什么时候足够好。请记住,当您阅读本文时,可以进行更多改进,但目标是制作 createAccount()
实现了可测试。
- This is my test but createAccount() doesn't have parameters, so how to add input to it for testing?
createAccount
从 self.view
获取信息.那是 RegisterUI
目的。 RegisterUI
的方法是交互式的,这使得它们难以在测试中使用。幸运的是,我们可以将任何我们喜欢的 View 传递给
RegisterController
.我们不测试 RegisterUI
,它应该有自己的测试,如何RegisterController
使用 RegisterUI
.所以我们将制作 RegisterUI
的版本只是为了测试和使用它。我们可以制作 Mock object响应
RegisterUI
的方法。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
等等。将其用作 Controller 的 View 并继续。ctrl = RegisterController(mock_view)
account = ctrl.createAccount()
assert account.email == email
assert account.password == password1
assert account.seckey == seckey
或者,您可以编写
RegisterUI
的子类仅用于测试在构造函数中使用其属性并覆盖 getEmail()
和 friend 退货。类似于模拟,但更有条理。
- Does createAccount() violate [Write functions that take input and return a result. No side effects.]? It doesn't have parameters that take input.
从技术上讲是的,但这是一个经验法则。你可以传入
view
而不是使用 self.view
,但 Controller 的全部意义在于弥合 View 和模型之间的差距。它可以访问 UI 是合适的。createAccount()
是积分函数。它封装了使用来自 UI 的信息创建帐户的过程;不需要了解 UI 的详细信息或帐户。这很好。您可以更改帐户创建过程以及调用 createAccount()
的所有内容仍然可以工作。
- "if" statement in createAccount() is control flow? If yes, [is this using exceptions for control flow?]
是的,一个
if
是控制流。但是createAccount()
没有对控制流使用异常。异常(exception)是针对特殊情况。
open
打开一个文件。如果它无法打开文件,则会出现异常。 createAccount()
创建一个帐户。如果创建异常帐户失败,则抛出异常。将此与
isEmailValid(email)
之类的函数进行对比.这是询问电子邮件是否有效。使用异常(exception)来指示无效电子邮件是不合适的;完全可以预期 isEmailValid(email)
将收到一封无效的电子邮件。无效的电子邮件是 isEmailValid
的正常情况。 .相反,它应该返回一个简单的 bool 值。但是,
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()
是在做。
- [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.
从外部角度来看,它只做一件事:它处理从用户输入创建帐户。究竟它是如何做到的,这是一个故意的黑匣子。这个information hiding意味着如果创建帐户的工作细节发生变化,对程序其余部分的影响是有限的。
如果稍后它决定一个帐户需要一个名称,您可以将其添加到
createAccount()
(和 RegisterUI.getName
)而不更改其界面。
- 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?
当我昨天处理你的代码时,我没有意识到
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()
叫他们让它更 slim 。def createAccount(self):
email = self.AskForValidEmail()
password = self.AskForValidPassword()
return self.makeAccount(email, password1, self.view.getSecKey())
测试
AskForValidEmail
你可以让一个鸽友RegisterUI
mock 。而不是 getEmail
只返回一个字符串,它可以在前两次调用时返回无效的电子邮件,在第三次调用时返回有效的电子邮件。
关于python - 如何编写注册账户测试用例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57369574/