Search code examples
c#unit-testingconsoleconsole-redirect

C# unit test for a method which calls Console.ReadLine()


I want to create a unit test for a member function of a class called ScoreBoard which is storing the top five players in a game.

The problem is that the method I created a test for (SignInScoreBoard) is calling Console.ReadLine() so the user can type their name:

public void SignInScoreBoard(int steps)
{
    if (topScored.Count < 5)
    {
        Console.Write(ASK_FOR_NAME_MESSAGE);
        string name = Console.ReadLine();
        KeyValuePair<string, int> pair = new KeyValuePair<string, int>(name, steps);
        topScored.Insert(topScored.Count, pair);
    }
    else
    {
        if (steps < topScored[4].Value)
        {
            topScored.RemoveAt(4);
            Console.Write(ASK_FOR_NAME_MESSAGE);
            string name = Console.ReadLine();
            topScored.Insert(4, new KeyValuePair<string, int>(name, steps));
        }
    }
}

Is there a way to insert like ten users so I can check if the five with less moves (steps) are being stored?


Solution

  • You'll need to refactor the lines of code that call Console.ReadLine into a separate object, so you can stub it out with your own implementation in your tests.

    As a quick example, you could just make a class like so:

    public class ConsoleNameRetriever {
         public virtual string GetNextName()
         {
             return Console.ReadLine();
         }
    }
    

    Then, in your method, refactor it to take an instance of this class instead. However, at test time, you could override this with a test implementation:

    public class TestNameRetriever : ConsoleNameRetriever {
         // This should give you the idea...
         private string[] names = new string[] { "Foo", "Foo2", ... };
         private int index = 0;
         public override string GetNextName()
         {
             return names[index++];
         }
    }
    

    When you test, swap out the implementation with a test implementation.

    Granted, I'd personally use a framework to make this easier, and use a clean interface instead of these implementations, but hopefully the above is enough to give you the right idea...