“Hello World” - TDD方式?

时间:2009-04-27 14:46:28

标签: c# unit-testing tdd nunit

自从我被介绍到TDD以来,我一直在思考这个问题。 哪个是构建“Hello World”应用程序的最佳方式?这将在控制台上打印“Hello World” - 使用测试驱动开发。

我的测试会是什么样子?什么课程?

请求:没有“类似维基百科的”指向TDD的链接,我熟悉TDD。只是好奇如何解决这个问题。

10 个答案:

答案 0 :(得分:21)

您需要在界面后面隐藏控制台。 (无论如何,这可能被认为是有用的)

撰写测试

[TestMethod]
public void HelloWorld_WritesHelloWorldToConsole()
{
  // Arrange
  IConsole consoleMock = MockRepository.CreateMock<IConsole>();

  // primitive injection of the console
  Program.Console = consoleMock;

  // Act
  Program.HelloWorld();

  // Assert
  consoleMock.AssertWasCalled(x => x.WriteLine("Hello World"));
}

编写程序

public static class Program
{
  public static IConsole Console { get; set; }

  // method that does the "logic"
  public static void HelloWorld()
  {
    Console.WriteLine("Hello World");
  }

  // setup real environment
  public static void Main()
  {
    Console = new RealConsoleImplementation();
    HelloWorld();
  }
}

重构更有用的东西; - )

答案 1 :(得分:5)

嗯......我没见过你的世界的TDD版本。但是,要看到一个类似于TDD和可管理性的简单问题,您可以查看Enterprise FizzBuzzcode)。至少这将使您能够看到在hello世界中可能实现的过度工程化水平。

答案 2 :(得分:5)

演示 - 视图? (模型似乎没有必要)

View是一个将输出传递给控制台的类(简单的单行方法)

Presenter是调用view.ShowText(“Hello World”)的接口,您可以通过提供存根视图来测试它。

为了提高工作效率,我只是写了该死的程序:)

单个测试应该足够(以伪代码形式):

IView view = Stub<IView>();
Expect( view.ShowText("Hello World") );

Presenter p = new Presenter( view );
p.Show();

Assert.IsTrue( view.MethodsCalled );

答案 3 :(得分:4)

的伪代码:

  • 创建一个接受流的东西的模拟。
  • 通过某种依赖注入(如构造函数参数)向此模拟调用helloworld。
  • 验证“Hello World”字符串是否已流入您的模拟。

在生产代码中,您使用提示而不是模拟。

经验法则:

  • 定义组件如何与其他内容交互的成功标准,而不仅仅是它与您的交互方式。 TDD专注于外部行为。
  • 设置环境(模拟)以处理事件链。
  • 运行它。
  • 验证

答案 4 :(得分:3)

一个非常有趣的问题。我不是一个庞大的TDD用户,但我会抛出一些想法。

我假设您要测试的应用程序是:

public static void Main()
{
    Console.WriteLine("Hello World");
}

现在,由于我无法想到任何直接测试的好方法,我会将编写任务分解为界面。

public interface IOutputWriter
{
    void WriteLine(string line);
}

public class ConsoleWriter : IOutputWriter
{
    public void WriteLine(string line)
    {
        Console.WriteLine(line);
    }
}

然后像这样打破应用程序

public static void Main()
{
    IOutputWriter consoleOut = new ConsoleWriter();
    WriteHelloWorldToOutput(consoleOut);
}

public static void WriteHelloWorldToOutput(IOutputWriter output)
{
    output.WriteLine("Hello World");
}

现在您有了一个方法注入点,允许您使用您选择的模拟框架断言使用“Hello World”参数调用WriteLine方法。

我未解决的问题(我对输入感兴趣):

  1. 如何测试ConsoleWriter类,我猜你仍然需要一些UI测试框架来实现这一目标,如果你有这样的话,那么整个问题无论如何都应该是......

  2. 测试主要方法。

  3. 为什么我觉得我已经通过将一行未经测试的代码更改为七行代码来实现某些功能,其中只有一行实际经过测试(尽管我认为覆盖已经了)

答案 5 :(得分:3)

假设您了解单元测试,并且假设您了解tdd“红绿重构过程”(因为您说您熟悉TDD),我会快速解释典型的tdd思维过程。

如果你想到一个特定的问题单元,那么你的TDD生活将会变得更容易,并且在依赖性方面应该考虑所有其他连接的东西。这是一个样本

情形: - 我希望我的程序在控制台上显示hello world。

tdd思考过程: -

“我认为我的程序将开始运行,然后调用控制台程序将我的消息传递给它,然后我希望我的控制台程序在屏幕上显示它”

“所以我需要测试一下,当我运行我的程序时,它应该调用控制台程序”

“现在有什么依赖关系?嗯我知道控制台程序就是其中之一。我不需要担心控制台如何将消息传递到屏幕上(调用io设备,打印等等) )我只需要知道我的程序成功调用了控制台程序。我需要相信控制台程序可以工作,如果没有,那么此刻我不负责测试并确保它有效。责任我想要测试的是我的程序在启动时调用控制台程序。“

“但我甚至不知道究竟要调用哪个控制台程序。我知道System.console.Writeline(具体实现)但是由于需求的变化,这可能会在未来发生变化,所以我该怎么办? “

“好吧,我将依赖于接口(或抽象)而不是具体实现,然后我可以创建一个虚拟控制台来实现我可以测试的接口”

  public interface Iconsole
    {
       void WriteToConsole(string msg);
    }



 public class FakeConsole : Iconsole
    {
        public bool IsCalled = false;

        public void WriteToConsole(string msg)
        {
            IsCalled = true;
        }
    }

我已经把IsCalled成员的“状态”改变了,如果调用控制台程序那么

好的我知道这听起来像是一个漫长的思考过程,但确实有回报。 Tdd强迫你在编码之前思考,然后在思考之前编码就好了

在一天结束时,您可能会想出以下方式来调用您的程序:

var console = new FakeConsole();
    console.IsCalled = false;
    my_program program = new my_program(console);
    program.greet();

我将控制台传递给my_program,以便my_program将使用控制台将我们的消息写入屏幕。

我的my_program可能如下所示:

public class my_program
    {

        Iconsole _consol;
        public my_program(Iconsole consol)
        {
            if (consol != null)
                _consol = consol;
        }
        public void greet()
        {
            _consol.WriteToConsole("Hello world");
        }
    }

最终的单元测试将是: -

 [TestMethod]
        public void myProgramShouldDisplayHelloWorldToTheConsole()
        {
            //arrange

            var console = new FakeConsole();
            console.IsCalled = false;
            my_program program = new my_program(console);
           //act
            program.greet();

            //assert
            Assert.AreEqual(true, console.IsCalled, " console was not called to display the greeting");



        }

答案 6 :(得分:2)

在java中,您可以捕获(“重定向”)System.out流并读取其内容。我确信在C#中也可以这样做。它只是Java中的几行代码,所以我相信它在C#中不会多得多

答案 7 :(得分:2)

我真的不得不反对这个问题!所有方法都有自己的位置,TDD在很多地方都很好。但是用户界面是我真正退出TDD的第一个地方。在我看来,这是MVC设计模式的最佳理由之一:以编程方式测试模型和控制器;目视检查您的视图。您所谈论的是对“Hello World”数据进行硬编码并测试它是否已进入控制台。要使用相同的源语言进行此测试,您几乎必须虚拟控制台对象,这是唯一可以执行任何操作的对象

或者,您可以在bash中编写测试脚本:

echo `java HelloWorldProgram`|grep -c "^Hello World$"

有点难以添加到JUnit测试套件,但有些东西告诉我,这绝不是计划....

答案 8 :(得分:1)

我同意David Berger;分离界面,并测试模型。在这种情况下,似乎“模型”是一个返回“Hello,world!”的简单类。测试看起来像这样(在Java中):

  Greeter greeter = new Greeter();
  assertEquals("Hello World!", greeter.greet());

我在http://ziroby.wordpress.com/2010/04/18/tdd_hello_world/创建了解决Hello World TDD风格的文章。

答案 9 :(得分:0)

我猜是这样的:

using NUnit.Framework;
using System.Diagnostics;

[TestFixture]
public class MyTestClass {
    [Test]
    public void SayHello() {
        string greet = "Hello World!";
        Debug.WriteLine(greet);
        Assert.AreEqual("Hello World!", greet);
    }
}