一年前,我对如何进行 TDD 几乎一无所知(但我真的很想(多么令人沮丧))并且从未听说过 BDD ......现在我都强迫性地这样做了。我一直在 .Net 开发环境中,而不是 Java,但我什至用宏替换了“F5 - 运行”按钮来运行 Cucumber (BDD) 或 MBUnit (TDD),具体取决于它是功能/场景还是规范。如果可能的话,没有调试器。如果您使用调试器(开玩笑(有点)),则 jar 中的 $1。
这个过程非常棒。我们另外使用的框架是我有幸遇到的 Oracle,并从中吸收信息,而他/我们使用的框架是 MavenThought。
一切都从 BDD 开始。我们的 BDD 是铁红宝石上的黄瓜。
特点:
场景:....
鉴于我做废话...
当我做其他事情时...
然后奇妙的事情发生了……
场景:...
这不是单元测试本身,而是逐个场景驱动功能,进而推动单元(测试)规范。因此,您从一个场景开始,并在场景中完成每个步骤驱动您的 TDD。
我们一直在使用的 TDD 在某种程度上是一种 BDD,因为我们着眼于 SUT(被测系统)要求的行为,并且每个规范都指定了一个行为(类“测试”文件)。
例子:
这是一种行为的规范:创建被测系统时。
还有一个规范(C#When_blah_happens 类文件)用于属性更改时的另一种行为,但它被分离到一个单独的文件中。
using MavenThought.Commons.Testing;
using SharpTestsEx;
namespace Price.Displacement.Module.Designer.Tests.Model.Observers
{
/// <summary>
/// Specification when diffuser observer is created
/// </summary>
[ConstructorSpecification]
public class When_diffuser_observer_is_created
: DiffuserObserverSpecification
{
/// <summary>
/// Checks the diffuser injection
/// </summary>
[It]
public void Should_return_the_injected_diffuser()
{
Sut.Diffuser.Should().Be.SameInstanceAs(this.ConcreteDiffuser);
}
}
}
这可能是 SUT 最简单的行为,因为在这种情况下,当它被创建时,Diffuser 属性应该与注入的扩散器相同。我不得不使用 Concrete Diffuser 而不是 Mock,因为在这种情况下,Diffuser 是一个核心/域对象,并且没有接口的属性通知。 95% 的情况下,我们引用所有依赖项,例如 Dep(),而不是注入真实的东西。
通常我们有不止一个 [It] Should_do_xyz(),有时还有一些设置,比如可能多达 10 行存根。这只是一个非常简单的示例,该规范中没有 GivenThat() 或 AndGivenThatAfterCreated()。
对于每个规范的设置,我们通常只需要覆盖规范的几个方法:
GivenThat() ==> 这发生在 SUT 创建之前。
CreatSut() ==> 我们使用 StructureMap 自动模拟创建 sut,并且 90% 的时间不需要覆盖它,但如果您是构造函数注入混凝土,则必须覆盖它。
AndGivenThatAfterCreated() => 这发生在 SUT 创建之后。
WhenIRun() => 除非它是 [ConstructorSpecification],否则我们使用它来运行我们为 SUT 指定的行为的一行代码
此外,如果同一 SUT 的两个或多个规范存在共同行为,我们会将其移至基本规范中。
运行规范我要做的就是突出显示它的名称,例如“When_diffuser_observer_is_created”,然后按 F5,因为请记住,对我来说,F5 运行 Rake 任务,如果 Cucumber 则为 test:feature[tag],或者 test:class[SUT ]。对我来说很有意义,因为每次运行调试器时都会被扔掉,不会创建任何代码(哦,它要花 1 美元(开玩笑))。
这是一种非常非常简洁的方式来指定 TDD 的行为,并且具有非常非常简单的 SUT 和简单的规范。如果您尝试成为牛仔程序员并编写具有硬依赖等的 SUT 蹩脚的东西,您将感到尝试执行 TDD 并厌倦/放弃或咬紧牙关做对的痛苦。
这是实际的 SUT。我们有点花哨,并使用 PostSharp 在扩散器上添加属性通知更改,因此 Post.Cast。再说一次,这就是我注入混凝土而不是模拟的原因。无论如何,正如您所看到的,在另一个规范中定义的缺失行为是当扩散器发生任何变化时。
using System.ComponentModel;
using MavenThought.Commons.Events;
using PostSharp;
using Price.Displacement.Core.Products;
using Price.Displacement.Domain;
namespace Price.Displacement.Desktop.Module.Designer.Model.Observers
{
/// <summary>
/// Implementation of current observer for the selected product
/// </summary>
public class DiffuserObserver : AbstractNotifyPropertyChanged, IDiffuserObserver
{
/// <summary>
/// gets the diffuser
/// </summary>
public IDiffuser Diffuser { get; private set; }
/// <summary>
/// Initialize with a diffuser
/// </summary>
/// <param name="diffuser">The diffuser to observe</param>
public void Initialize(IDiffuser diffuser)
{
this.Diffuser = diffuser;
this.NotifyInterface().PropertyChanged += (x, e) => this.OnPropertyChanged(e.PropertyName);
}
/// <summary>
/// Gets the notify interface to use
/// </summary>
/// <returns>The instance of notify property changed interface</returns>
protected INotifyPropertyChanged NotifyInterface()
{
return Post.Cast<Diffuser, INotifyPropertyChanged>((Diffuser)Diffuser);
}
}
}
总之,这种 BDD / TDD 开发风格令人震撼。花了一年时间,但作为一种生活方式,我完全皈依了。我自己不会学到这一点。我从 The Oracle http://orthocoders.com/ 那里得到了所有东西。
红色或蓝色药丸,由您选择。