Rob*_*kal 8 python bdd nose nosetests python-behave
我一直在考虑从鼻子切换到测试行为(摩卡/柴等等已经破坏了我).到目前为止一直很好,但我似乎无法找出除例外测试异常的方法:
@then("It throws a KeyError exception")
def step_impl(context):
try:
konfigure.load_env_mapping("baz", context.configs)
except KeyError, e:
assert (e.message == "No baz configuration found")
Run Code Online (Sandbox Code Playgroud)
用鼻子我可以用一个注释来测试
@raises(KeyError)
Run Code Online (Sandbox Code Playgroud)
我在行为中找不到这样的东西(不在源中,不在示例中,不在此处).能够指定可能在场景轮廓中抛出的异常肯定会很棒.
有人走过这条路吗?
我自己对BDD很陌生,但通常情况下,测试会记录客户可以预期的行为 - 而不是步骤实现.所以我希望通过规范的方式测试它会是这样的:
When I try to load config baz
Then it throws a KeyError with message "No baz configuration found"
Run Code Online (Sandbox Code Playgroud)
使用以下定义的步骤:
@when('...')
def step(context):
try:
# do some loading here
context.exc = None
except Exception, e:
context.exc = e
@then('it throws a {type} with message "{msg}"')
def step(context, type, msg):
assert isinstance(context.exc, eval(type)), "Invalid exception - expected " + type
assert context.exc.message == msg, "Invalid message - expected " + msg
Run Code Online (Sandbox Code Playgroud)
如果这是一个常见的模式,你可以编写自己的装饰器:
def catch_all(func):
def wrapper(context, *args, **kwargs):
try:
func(context, *args, **kwargs)
context.exc = None
except Exception, e:
context.exc = e
return wrapper
@when('... ...')
@catch_all
def step(context):
# do some loading here - same as before
Run Code Online (Sandbox Code Playgroud)
Barry 的这种 try/catch 方法有效,但我发现了一些问题:
@where我的建议是
代码:
def given(regexp):
return _wrapped_step(behave.given, regexp) #pylint: disable=no-member
def then(regexp):
return _wrapped_step(behave.then, regexp) #pylint: disable=no-member
def when(regexp):
return _wrapped_step(behave.when, regexp) #pylint: disable=no-member
def _wrapped_step(step_function, regexp):
def wrapper(func):
"""
This corresponds to, for step_function=given
@given(regexp)
@accept_expected_exception
def a_given_step_function(context, ...
"""
return step_function(regexp)(_accept_expected_exception(func))
return wrapper
def _accept_expected_exception(func):
"""
If an error is expected, check if it matches the error.
Otherwise raise it again.
"""
def wrapper(context, *args, **kwargs):
try:
func(context, *args, **kwargs)
except Exception, e: #pylint: disable=W0703
expected_fail = context.expected_fail
# Reset expected fail, only try matching once.
context.expected_fail = None
if expected_fail:
expected_fail.assert_exception(e)
else:
raise
return wrapper
class ErrorExpected(object):
def __init__(self, message):
self.message = message
def get_message_from_exception(self, exception):
return str(exception)
def assert_exception(self, exception):
actual_msg = self.get_message_from_exception(exception)
assert self.message == actual_msg, self.failmessage(exception)
def failmessage(self, exception):
msg = "Not getting expected error: {0}\nInstead got{1}"
msg = msg.format(self.message, self.get_message_from_exception(exception))
return msg
@given('the next step shall fail with')
def expect_fail(context):
if context.expected_fail:
msg = 'Already expecting failure:\n {0}'.format(context.expected_fail.message)
context.expected_fail = None
util.show_gherkin_error(msg)
context.expected_fail = ErrorExpected(context.text)
Run Code Online (Sandbox Code Playgroud)
我导入我修改过的 given/then/when 而不是行为,并添加到我的 environment.py 启动 context.expected 在场景之前失败并在之后检查它:
def after_scenario(context, scenario):
if context.expected_fail:
msg = "Expected failure not found: %s" % (context.expected_fail.message)
util.show_gherkin_error(msg)
Run Code Online (Sandbox Code Playgroud)
您展示的 try / except 方法实际上是完全正确的,因为它显示了您在现实生活中实际使用代码的方式。然而,有一个你不完全喜欢它的原因。它会导致如下丑陋的问题:
Scenario: correct password accepted
Given that I have a correct password
When I attempt to log in
Then I should get a prompt
Scenario: incorrect password rejected
Given that I have an incorrect password
When I attempt to log in
Then I should get an exception
Run Code Online (Sandbox Code Playgroud)
如果我在没有 try/ except 的情况下编写步骤定义,那么第二种情况将会失败。如果我用 try/ except 编写它,那么第一个场景可能会隐藏异常,特别是如果异常发生在提示已经打印之后。
相反,恕我直言,这些场景应该写成类似的内容
Scenario: correct password accepted
Given that I have a correct password
When I log in
Then I should get a prompt
Scenario: correct password accepted
Given that I have a correct password
When I try to log in
Then I should get an exception
Run Code Online (Sandbox Code Playgroud)
“我登录”步骤不应使用 try;“我尝试登录”与“尝试”完美匹配,并泄露了可能不会成功的事实。
然后就出现了两个几乎但不完全相同的步骤之间的代码重用的问题。也许我们不希望有两个功能同时登录。除了简单地调用一个常见的其他函数之外,您还可以在步骤文件末尾附近执行类似的操作。
@when(u'{who} try to {what}')
def step_impl(context):
try:
context.execute_steps("when" + who + " " + what)
context.exception=None
except Exception as e:
context.exception=e
Run Code Online (Sandbox Code Playgroud)
这将自动将包含“try to”一词的所有步骤转换为具有相同名称但删除了 try 的步骤,然后使用 try/ except 保护它们。
关于何时应该在 BDD 中处理异常存在一些问题,因为它们对用户不可见。但这不是这个问题答案的一部分,所以我将它们放在单独的帖子中。
| 归档时间: |
|
| 查看次数: |
4765 次 |
| 最近记录: |