-3

I have the following method, which I want to perform a unit test on. Change can be made for the method signature:

public void PrintNumber()
{
    Enumerable.Range(1, 100).ToList().ForEach(x =>
    {
        if (x % 3 == 0 && x % 5 == 0)
            Console.WriteLine("[35]");
        else if (x % 3 == 0)
            Console.WriteLine("[3]");
        else if (x % 5 == 0)
            Console.WriteLine("[5]");
        else
            Console.WriteLine(x.ToString());
    });
}

I have my own solution, but I want to find out if my version is the best.

Thanks!

Charles
  • 50,943
  • 13
  • 104
  • 142
Pingpong
  • 7,681
  • 21
  • 83
  • 209

3 Answers3

8

In order to unit test this method, you need to wrap your Console.WriteLine() with a proxy class, and inject that class into your method.

public interface IWriter
{
    void Write(string text);
}

public class ConsoleWriter : IWriter
{
    public void Write(string text)
    {
       Console.WriteLine(text);
    }
}

public class StubWriter : IWriter
{
    List<string> values = new List<string>();
    public void Write(string text)
    {
         values.Add(text);
    }

    public List<string> Values { get { return values; } }
}

With this structure you change your method signature to PrintNumber(IWriter writer) and call the writer. In your test method you inject the StubWriter, in production you inject the ConsoleWriter.

Charles Graham
  • 1,157
  • 5
  • 6
  • +1, although I dont think the OP put any time into even bother solving this... Plus the +1 is because I dont think the OP will mark any of these as answers... – Jeremy Thompson Aug 16 '11 at 02:19
  • Yes for sure he wont bother understanding this and it might look a bit diff in understanding this i guess :) Btw good answer. – Zenwalker Aug 16 '11 at 03:11
  • @Charles Graham Thank you for yoru solution. I have my own solution, but I want to find out if my version is the best. – Pingpong Aug 16 '11 at 12:33
  • Although this solution is correct, it can also be achieved by redirecting the `Console` class output using the `Console.SetOut()` method. – Bernard Aug 16 '11 at 18:58
2

Here's a way to rewrite your method

public void PrintNumber(TextWriter writer, 
               Action<TextWriter, int> modHandler) {... }

public void HandleMod(TextWriter writer, int input) {...}

And some tests:

void Print_Number_Should_Enumerate_0to100 {...}
void Handle_Mod_Outputs_35_Only_When_Input_35 {...}
void Handle_Mod_Gets_Mod_3_Correct {...}
void Handle_Mode_Gets_Mod_5_Correct {...}
void Handle_Mode_Defaults_To_Outputting_Input {...}

And so on.

However, you can't TDD this - it's been written already. TDD involves writing the tests first. What you can do is write the tests, then refactor to make the tests work.

Philip Rieck
  • 32,368
  • 11
  • 87
  • 99
1

If you mean unit test, you'd only be able to validate the output, as the method has no input. I'd rewrite the signature as:

public void PrintNumber(TextWriter writer) // use writer.WriteLine instead of console.WriteLine

and then in the test pass in something like a StreamWriter hooked to a MemoryStream. After calling the method from the test method, the output should be written to the MemoryStream, and you could validate that it produced the output that you expected.

EDIT: Neglected to add, in production if you really wanted it to write to the console, call it as follows:

PrintNumber(Console.Out);
Chris Shain
  • 50,833
  • 6
  • 93
  • 125