15

Well I have been thinking about this for a while, ever since I was introduced to TDD. Which would be the best way to build a "Hello World" application ? which would print "Hello World" on the console - using Test Driven Development.

What would my Tests look like ? and Around what classes ?

Request: No "wikipedia-like" links to what TDD is, I'm familiar with TDD. Just curious about how this can be tackled.

abhilash
  • 5,605
  • 3
  • 36
  • 59
  • 19
    I am crying on the inside. And on the outside. – TheTXI Apr 27 '09 at 14:50
  • 4
    I certainly hope you are not asking this so that you can use the answer to demonstrate TDD to someone who does not understand TDD. I'm pretty certain that however good an answer you can get, it will devalue TDD and the target audience will think TDD is just a way to make simple things hard. – Mikko Rantanen Apr 27 '09 at 14:58
  • The problem is that "Hello World" is a terrible example to use with TDD. You really need something that generates variable output based on variable input. – 17 of 26 Apr 27 '09 at 15:09
  • Yes, I completely agree that Hello World is not a good example to use with TDD. But as the question said, I was curious how this could be tackled using TDD. – abhilash Apr 27 '09 at 17:29
  • 3
    Hm, I'm just not really sure if it is such a bad example. Because in real applications, you are often too strongly coupled to things that can not be mocked, like the console here. These are for instance file access, database, network or libraries that use theses. It's always the first question: "and how do I test THIS?" Yes, there should be a minimum of logic to demonstrate TDD, Hello World has rather few. but for me the simple examples never really worked anyway. It could be a start to add more logic to it. – Stefan Steinegger Apr 27 '09 at 19:58

10 Answers10

21

You need to hide the Console behind a interface. (This could be considered to be useful anyway)

Write a Test

[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"));
}

Write the program

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();
  }
}

Refactor to something more useful ;-)

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • 1
    I would just inject and mock System.IO.TextWriter, which is an abstract type implemented by `System.Console.Out`. No need to write `IConsole` and `RealConsoleImplementation` that way – Wim Coenen May 25 '09 at 16:22
  • 1
    It depends. I like to implement against interfaces. If there isn't any, I write sometimes my own, as small as I need it. Why using a large, general purpose interface, if you only need a few simple methods? And abstract base classes include logic. The caller should really not care who is implementing "WriteLine". – Stefan Steinegger May 25 '09 at 17:02
  • 1
    Pardon, where is `IConsole` defined? Is this a .Net Framework class I've been missing out on? –  Jul 22 '13 at 13:56
  • @abyrob: no, not that I'm aware of, you have to write it yourself. – Stefan Steinegger Aug 05 '13 at 07:19
5

Well...I've not seen a TDD version of hello world. But, to see a similarly simple problem that's been approached with TDD and manageability in mind, you could take a look at Enterprise FizzBuzz (code). At least this will allow you to see the level of over-engineering you could possibly achieve in a hello world.

Shog9
  • 156,901
  • 35
  • 231
  • 235
dustyburwell
  • 5,755
  • 2
  • 27
  • 34
  • Thanks ascalonx, Well, thinking of it, even i have yet to encounter a TDD version of Hello World. – abhilash Apr 27 '09 at 14:53
  • That blog is now offline but there's a [FizzBuzz Enterprise Edition](https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition/blob/master/src/test/java/FizzBuzzTest.java) on Github. Ugh. – Jamie Bull Apr 08 '14 at 12:29
5

Presenter-View? (model doesn't seem strictly necessary)

View would be a class that passes the output to the console (simple single-line methods)

Presenter is the interface that calls view.ShowText("Hello World"), you can test this by providing a stub view.

For productivity though, I'd just write the damn program :)

A single test should suffice (in pseudocode):

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

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

Assert.IsTrue( view.MethodsCalled );
Lennaert
  • 2,455
  • 15
  • 15
  • 1
    MVP for Hello World ? Overkill! – Gishu Apr 27 '09 at 15:04
  • Because going all the way makes TDD look like an academic exercise rather than a useful technique. Wait, i can't believe I'm actually arguing educational value on "Hello World, the TDD Way" – Arne Claassen May 30 '11 at 06:55
4

Pseudo-code:

  • Create a mock of something that accepts a stream.
  • Invoke helloworld onto this mock through some sort of dependency injection (Like a constructor argument).
  • Verify that the "Hello World" string was streamed into your mock.

In production code, you use the prompt instead of the mock.

Rule of thumb:

  • Define your success criteria in how the component interacts with other stuff, not just how it interacts with you. TDD focuses on external behavior.
  • Set up the environment (mocks) to handle the chain of events.
  • Run it.
  • Verify.
Tormod
  • 4,551
  • 2
  • 28
  • 50
3

Assuming you know unit testing, and asuming you understand the tdd "red green refactor process" (since you said you are familiar with TDD) Ill quickly explain a typical tdd thought process.

Your TDD life will be made a lot easier if you think of a particular unit of problem and every other connected things should be thought of in terms of dependencies. here is a sample

scenario:- I want my program to display hello world on the console.

tdd thought process:-

"I think my program will start running then call the console program passing my message to it and then I expect my console program to display it on the screen"

"so i need to test that when i run my program, it should call the console program "

"now what are the dependencies? hmm I know that the console program is one of them. I don't need to worry about how the console will get the message to the screen (calling the io device, printing and all that) I just need to know that my program successfully called the console program. I need to trust that the console program works and if it doesn't, then at the moment i am not responsible for testing and making sure it works. the responsibility I want to test is that my program when it starts up calls the console program. "

"but i don't even know exactly what console program to call. well I know of System.console.Writeline (concrete implementation) but then this may change in future due to change in requirements, so what do i do?"

"Well , I will depend on interface (or abstraction) rather than concrete implementation, then i can create a fake console implementing the interface which i can test against"

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



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

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

I have put IsCalled member whose "state" will change if whe ever the console program is called

OK I know it sounds like a long thought process but it does pay off. Tdd forces you to think before codeing which is better then coding before thinking

At the end of the day, you may then come up with something like the following way to invoke your program:

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

I passed console to my_program so that my_program will use console to write our message to the screen.

and my my_program may look like this:

public class my_program
    {

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

the final unit test will then be :-

 [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");



        }
AroglDarthu
  • 1,021
  • 8
  • 17
Samuel
  • 1,295
  • 16
  • 21
3

A very interesting question. I'm not a huge TDD user, but I'll throw some thoughts out.

I'll assume that the application that you want to test is this:

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

Now, since I can't think of any good way of testing this directly I'd break out the writing task into an interface.

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

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

And break the application down like this

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

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

Now you have an injection point to the method that allows you to use the mocking framework of your choice to assert that the WriteLine method is called with the "Hello World" parameter.

Problems that I have left unsolved (and I'd be interested in input):

  1. How to test the ConsoleWriter class, I guess you still need some UI testing framework to achieve this, and if you had that then the whole problem in moot anyway...

  2. Testing the main method.

  3. Why I feel like I've achieved something by changing one line of untested code into seven lines of code, only one of which is actually tested (though I guess coverage has gone up)

Martin Harris
  • 28,277
  • 7
  • 90
  • 101
2

In java you could capture ("redirect") the System.out stream and read its contents. I'm sure the same could be done in C#. It's only a few lines of code in java, so I'm sure it won't be much more in C#

krosenvold
  • 75,535
  • 32
  • 152
  • 208
  • 1
    You can set the output stream to any TextWriter so the following should work. TextWriter output = new StringWriter(); Console.SetOut(output); //Call your hello world method here Assert("Hello World", output.ToString()); – Martin Harris Apr 27 '09 at 15:18
  • Thanks. That's not much of a test fixture ;) – krosenvold Apr 27 '09 at 15:20
2

I really have to object to the question! All methodologies have their place, and TDD is good in a lot of places. But user interfaces is the first place I really back off from TDD. This, in my humble opinion is one of the best justifications of the MVC design pattern: test the heck out of your models and controller programmatically; visually inspect your view. What you're talking about is hard-coding the data "Hello World" and testing that it makes it to the console. To do this test in the same source language, you pretty much have to dummy the console object, which is the only object that does anything at all.

Alternately, you can script your test in bash:

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

A bit difficult to add to a JUnit test suite, but something tells me that was never the plan....

David Berger
  • 12,385
  • 6
  • 38
  • 51
1

I agree with David Berger; separate off the interface, and test the model. It seems like the "model" in this case is a simple class that returns "Hello, world!". The test would look like this (in Java):

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

I've created a write up of solving Hello World TDD style at http://ziroby.wordpress.com/2010/04/18/tdd_hello_world/ .

zhon
  • 1,610
  • 1
  • 22
  • 31
Ron Romero
  • 9,211
  • 8
  • 43
  • 64
0

I guess something like this:

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);
    }
}
Jhonny D. Cano -Leftware-
  • 17,663
  • 14
  • 81
  • 103