- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我可以编写一些unittests,但不知道如何编写createAccount()的测试,它将其他函数连接在一起。
createAccount()包含以下步骤:
验证电子邮件
验证密码
检查密码匹配
实例化新帐户对象
每个步骤都有一些测试用例。
所以,我的问题是:
一。如何编写createAccount()测试用例?我应该列出所有可能的组合测试用例然后测试它们。
例如:
测试用例0电子邮件无效
测试用例1应用程序在重试电子邮件3次后停止
测试案例2电子邮件正常,密码无效
测试案例3。电子邮件正常,密码有效,第二个密码与第一个密码不匹配
测试案例4。电子邮件正常,密码有效,两个密码都匹配,安全性有效
测试案例5电子邮件正常,密码有效,两个密码都匹配,安全性有效,帐户创建成功
我不知道怎么测试吗,因为我的createAccount()很烂?如果是,如何重构它以便于测试?
这是我的代码:
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):
"""get email from user, check email
"""
self.email = email
email_obj = Email(self.email)
status = email_obj.isValidEmail() and not accounts.isDuplicate(self.email)
if not status:
raise EmailNotOK("Email is duplicate or incorrect format")
else:
return True
def ValidatePassword(self, password):
"""
get password from user, check pass valid
"""
self.password = password
status = Password.isValidPassword(self.password)
if not status:
raise PassNotValid("Pass isn't valid")
else: return True
def CheckPasswordMatch(self, password):
"""
get password 2 from user, check pass match
"""
password_2 = password
status = Password.isMatch(self.password, password_2)
if not status:
raise PassNotMatch("Pass doesn't match")
else: return True
def createAccount(self):
retry = 0
while 1:
try:
email_input = self.view.getEmail()
self.ValidateEmail(email_input) #
break
except EmailNotOK as e:
retry = retry + 1
self.displaymessage(str(e))
if retry > 3:
return
while 1:
try:
password1_input = self.view.getPassword1()
self.ValidatePassword(password1_input)
break
except PassNotValid as e:
self.displaymessage(str(e))
while 1:
try:
password2_input = self.view.getPassword2()
self.CheckPasswordMatch(password2_input)
break
except PassNotMatch as e:
self.displaymessage(str(e))
self.seckey = self.view.getSecKey()
account = Account(Email(self.email), Password(self.password), self.seckey)
message = "Account was create successfully"
self.displaymessage(message)
return account
class Register(Option):
def execute(self):
view = RegisterUI()
controller_one = RegisterController(view)
controller_one.createAccount()
"""========================Code End=============================="""
"""Testing"""
@pytest.fixture(scope="session")
def ctrl():
view = RegisterUI()
return RegisterController(view)
def test_canThrowErrorEmailNotValid(ctrl):
email = 'dddddd'
with pytest.raises(EmailNotOK) as e:
ctrl.ValidateEmail(email)
assert str(e.value) == 'Email is duplicate or incorrect format'
def test_EmailIsValid(ctrl):
email = 'hello@gmail.com'
assert ctrl.ValidateEmail(email) == True
def test_canThrowErrorPassNotValid(ctrl):
password = '123'
with pytest.raises(PassNotValid) as e:
ctrl.ValidatePassword(password)
assert str(e.value) == "Pass isn't valid"
def test_PasswordValid(ctrl):
password = '1234567'
assert ctrl.ValidatePassword(password) == True
def test_canThrowErrorPassNotMatch(ctrl):
password1= '1234567'
ctrl.password = password1
password2 = 'abcdf'
with pytest.raises(PassNotMatch) as e:
ctrl.CheckPasswordMatch(password2)
assert str(e.value) == "Pass doesn't match"
def test_PasswordMatch(ctrl):
password1= '1234567'
ctrl.password = password1
password2 = '1234567'
assert ctrl.CheckPasswordMatch(password2)
最佳答案
注意:我不太了解python,但我知道测试。我的python可能不完全正确,但是技术是正确的。
答案在于你对createAccount
的描述。它做的事情太多了。它有各种验证方法的包装器它显示消息。它创建一个帐户。它需要重构才能测试。测试和重构是齐头并进的。
首先,对四个部分中的每一个执行Extract Method refactoring以将它们转换为自己的方法。我只做三个验证步骤中的一个,它们基本上都是一样的因为这是一个死记硬背的操作,我们可以安全地做。Your IDE might even be able to do the refactor for you。
def tryValidatePassword(self):
while 1:
try:
password1_input = self.view.getPassword1()
self.ValidatePassword(password1_input)
break
except PassNotValid as e:
self.displaymessage(str(e))
def makeAccount(self):
return Account(Email(self.email), Password(self.password), self.seckey)
def createAccount(self):
self.tryValidatePassword()
self.seckey = self.view.getSecKey()
account = self.makeAccount()
message = "Account was create successfully"
self.displaymessage(message)
return account
createAccount
如果密码错误,它不会停止。
tryValidatePassword
并对其进行测试,我们看到如果密码无效,它将进入无限循环那不好。我不知道这个循环的目的是什么,所以我们把它去掉。
def tryValidatePassword(self):
try:
password1_input = self.view.getPassword1()
self.ValidatePassword(password1_input)
except PassNotValid as e:
self.displaymessage(str(e))
ValidatePassword
的包装器,打印异常。这揭示了一些反模式。
ValidatePassword
和其他人正在对控制流使用异常对于验证方法来说,发现事物是无效的并不例外。它们应该返回一个简单的布尔值这简化了事情。
def ValidatePassword(self, password):
"""
get password from user, check pass valid
"""
self.password = password
return Password.isValidPassword(self.password)
ValidatePassword
正在做两件不相关的事情:设置密码和验证密码。设置密码应该在其他地方进行。
def ValidatePassword(self, password):
return Password.isValidPassword(self.password)
def tryValidatePassword(self):
password1_input = self.view.getPassword1()
if !self.ValidatePassword(password1_input):
self.displaymessage("Pass isn't valid")
def tryValidatePassword(self, password):
if !self.ValidatePassword(password):
self.displaymessage("Pass isn't valid")
self.tryValidatePassword(self.view.getPassword1())
createAccount
到底在做什么?
self.view
获取内容并将其设置为
self
。
class RegisterController:
# Thin wrappers to hide the details of the validation implementations.
def ValidatePassword(self, password):
return Password.isValidPassword(password)
# If there needs to be retries, they would happen in here.
def ValidateEmail(self, email_string):
email = Email(email_string)
return email.isValidEmail() and not accounts.isDuplicate(email_string)
def CheckPasswordMatch(self, password1, password2):
return Password.isMatch(password1, password2)
# A thin wrapper to actually make the account from valid input.
def makeAccount(self, email, password, seckey):
return Account(Email(email), Password(password), seckey)
def createAccount(self):
password1 = self.view.getPassword1()
password2 = self.view.getPassword2()
if !self.ValidatePassword(password1):
self.displaymessage("Password is not valid")
return
if !self.CheckPasswordMatch(password1, password2):
self.displaymessage("Passwords don't match")
return
email = self.view.getEmail()
if !self.ValidateEmail(email):
self.displaymessage("Email is duplicate or incorrect format")
return
account = self.makeAccount(email, password, self.view.getSecKey())
self.displaymessage("Account was created successfully")
return
makeAccount
也很容易测试,它接受输入并返回一个帐户(或者不返回)。
createAccount
仍然做得太多。它处理从视图创建帐户的过程,同时也显示消息我们得把他们分开。
CreateAccountFailed
的子类。
# This is just a sketch.
class CreateAccountFailed(Exception):
pass
class PassNotValid(CreateAccountFailed):
pass
class PassNotMatch(CreateAccountFailed):
pass
class EmailNotOK(CreateAccountFailed):
pass
createAccount
可以在创建帐户失败时抛出特定版本的
CreateAccountFailed
异常这有很多好处。打电话
createAccount
比较安全它更灵活。我们可以把错误处理分开。
def createAccount(self):
password1 = self.view.getPassword1()
password2 = self.view.getPassword2()
if !self.ValidatePassword(password1):
raise PassNotValid("Password is not valid")
if !self.CheckPasswordMatch(password1, password2):
raise PassNotMatch("Passwords don't match")
email = self.view.getEmail()
if !self.ValidateEmail(email):
raise EmailNotOK("Email is duplicate or incorrect format")
return self.makeAccount(email, password, self.view.getSecKey())
# A thin wrapper to handle the display.
def tryCreateAccount(self):
try
account = self.createAccount()
self.displaymessage("Account was created successfully")
return account
except CreateAccountFailed as e:
self.displaymessage(str(e))
createAccount
可以很容易地进行单元测试!测试它将按预期创建帐户使它抛出各种异常验证方法得到自己的单元测试。
tryCreateAccount
也可以测试
Mock displaymessage
并检查它是否在正确的情况下使用正确的消息调用。
关于python - 如何用pytest编写正确的测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57351729/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!