我已经看到了我认为BDD的两种方法.差异取决于"何时"的位置:
在方法1中,when是规范的一部分:
AnEmptyStack.isNoLongerEmptyAfterPush
Run Code Online (Sandbox Code Playgroud)
在纯粹的"当时给定"术语中,这是:
"给定一个空堆栈,当它被推动时,它就不再是空的."
所以"when"是规范方法的一部分:
isNoLongerEmptyAfterPush(){
stack.push(anObject);
Assert.notEmpty(stack);
}
Run Code Online (Sandbox Code Playgroud)
在方法2中,when在类级别定义.也就是说,通常在设置中调用when.
class WhenAnEmptyStackIsPushed(){
setup(){
stack.push();
}
public void thenItIsNotEmpty(){
assert(stack.notEmpty())
}
}
Run Code Online (Sandbox Code Playgroud)
有首选方法吗?就纯行为测试而言,第二种选择对我来说似乎更为可取,因为测试夹具的重点在于行为.
但是,为了便于测试,我倾向于第一种方法.我在测试中发现的大部分痛苦都是设置.也就是说,我必须在特定状态下获得SUT.一旦进入该状态,通常只需要一行代码来实际调用它上面的某些行为.因此,每个类具有多个行为(即,每个设置上下文)利用该类的一次性设置.
所以,我正在寻找想法.一种方法比另一种方法更受欢迎吗?
根据您的测试框架,您也许可以两全其美。
当我围绕 sut 创建一组测试时,我首先声明一个类来包装整套规范,然后声明一个抽象类:
public class SomethingDoerSpecs
{
public abstract class concern : observations_for_a_sut_with_a_contract<IDoSomething,SomethingDoer>
{
// here I can define setup that will be common to all subsequent tests
context c = () => ...
}
public class When_asked_to_do_something : concern
{
context c = () =>
{
// setup specific to this context goes here
};
because b = () => sut.DoSomething();
it should_open_a_database_connection =
() => mock_db_connection.was_told_to(x => x.Open());
it should_set_the_result_value_to_true =
() => sut.Result.should_be_true();
// etc.
}
public class When_asked_to_do_something_but_the_database_is_unavailable
: When_asked_to_do_something
{
context c = () =>
{
// additional context
};
because b = doing(() => sut.DoSomething());
it should_throw_a_custom_exception = () =>
{
exception_thrown_by_the_sut.should_not_be_null();
exception_thrown_by_the_sut
.should_be_an_instance_of<CouldNotDoSomethingException>();
};
}
}
Run Code Online (Sandbox Code Playgroud)
这只是为了说明测试类通常可以嵌套,因此您仍然可以执行“大”When... 并在需要更大的上下文特异性时重用之前通过继承设置的状态。当然,您必须确保您的框架将重置断言集之间的设置。
顺便说一句,我在这里展示的整个委托语法来自 Jean-Paul Boodhoo 的 DevelopWithPassion.Bdd 库,您可以在 Github 上找到该库。