如何开始使用TDD编码一些简单功能?


9

我基本上有TDD的要旨。我卖了它很有用,并且对MSTEST框架有了一个合理的命令。但是,到目前为止,我还不能毕业于将其用作主要开发方法。通常,我将其用作编写控制台应用程序作为测试驱动程序的替代方法(我的传统方法)。

对我而言,最有用的是它吸收回归测试作用的方式。

我还没有构建专门隔离各种可测试行为的工具,这是我所知道的另一大部分。

因此,这个问题是要寻求关于我可能为以下开发任务编写的第一个测试的指针:我想生成以生产者/消费者的方式封装任务执行的代码。

我停下脚步,并决定在编写此代码后写这个问题(想知道这次是否可以真正使用TDD)

码:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

您应该先编写测试,然后编写接口!整个想法是TDD用于您的API。

1
如何编写尚不存在的接口的测试?他们甚至不会编译。
罗伯特·哈维

5
那是您的第一个失败的测试。
cori 2012年

Answers:


10

从这个概念
开始:1)从您想要的行为开始。为此编写一个测试。查看测试失败。
2)编写足够的代码以使测试通过。查看所有测试通过。
3)查找冗余/草率代码->重构。看到测试仍然通过。转到1

因此,在#1上,假设您要创建一个新命令(我正在扩展该命令的工作方式,所以请耐心等待)。(此外,我会有点务实而不是极端的TDD)

新命令称为MakeMyLunch,因此您首先创建一个测试以将其实例化并获取命令名称:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

这将失败,迫使您创建新的命令类并使其返回其名称(纯粹主义者会说这是两回合的TDD,而不是1)。因此,您可以创建该类并使其实现ICommand接口,包括返回命令名称。现在,运行所有测试将显示所有通过,因此您可以继续寻找重构机会。可能没有。

因此,接下来您希望它实现执行。因此,您必须问:我如何知道“ MakeMyLunch”成功“制作了我的午餐”。由于此操作,系统会发生什么变化?我可以对此进行测试吗?

假设很容易测试:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

在其他时候,这更困难,而您真正想做的是测试被测对象的职责(MakeMyLunchCommand)。MakeMyLunchCommand的职责可能是与Fridge和Microwave交互。因此,要进行测试,您可以使用模拟冰箱和模拟微波。[两个示例模拟框架是MockitonMock在这里查看。]

在这种情况下,您将执行以下伪代码:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

纯粹主义者说测试您班级的责任-它与其他班级的互动(命令是否打开了冰箱并打开了微波炉?)。

实用主义者说,要测试一组课程并测试结果(您的午餐准备好了吗?)。

找到适合您系统的合适平衡。

(注意:考虑到您可能还为时过早。也许您可以在编写单元测试和实现时让它发展,并在步骤3中“注意到”常见的接口机会)。


如果我还没有预先编写接口,是什么问题导致了Execute()方法的创建-当我没有刺激其他功能的“步骤”时,我对TDD的最初尝试就停了下来-感觉有一个潜在的鸡/蛋问题,必须回避
Aaron Anodide 2012年

1
好问题!如果您执行的唯一命令是“ MakeMyLunchCommand”,则该方法可能以“ .makeMyLunch()”开始。那本来很好。然后,您执行另一个命令(“ NapCommand.takeNap()”)。使用不同方法仍然没有问题。然后,您开始在您的生态系统中使用它,这很可能是您不得不推广到ICommand接口的地方。通常,您通常将泛化推迟到最后一个负责任的时刻,因为YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =)在其他时候,您知道您将到达那里,所以从此开始。
jayraynet'4

(这里还假设您使用的是现代IDE,使得重构诸如方法名之类的事情变得微不足道)
jayraynet 2012年

1
再次感谢你的建议,它是一种对我的一个里程碑终于看到所有的作品,以及他们如何适应-是的,快速的重构是在我的工具箱
亚伦Anodide
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.