iAteABug_And_iLiked_it
iAteABug_And_iLiked_it

Reputation: 3795

How to unit test "console.readLine" for invalid input?

I am scratching my head on this little problem.

I have this line and 3 other similar ones in a method

try
{
    int PhoneIMEINumber = int.Parse(Console.ReadLine());
}

{
catch(Exception)
{
    return null;
}

If the user enters "abcd" for input, this throws an exception and I can catch it and show an error message.

But how do I make a unit test for this? I can't simulate console input from the unit test ofcourse and I want to check from my unit test if a null was returned.

Thank you

Upvotes: 6

Views: 8384

Answers (4)

Dustin Kingen
Dustin Kingen

Reputation: 21245

If you want to Unit Test the Console then you'll probably need to create a wrapper interface.

public interface IConsole
{
    string ReadLine();
}

public class ConsoleWrapper : IConsole
{
    public string ReadLine()
    {
        return Console.ReadLine();
    }
}

This way you can create a Stub/Fake to test your business rules.

public class TestableConsole : IConsole
{
    private readonly string _output;

    public TestableConsole(string output)
    {
        _output = output;
    }

    public string ReadLine()
    {
        return _output;
    }
}

Inside the test:

public class TestClass
{
    private readonly IConsole _console;

    public TestClass(IConsole console)
    {
        _console = console;
    }

    public void RunBusinessRules()
    {
        int value;
        if(!int.TryParse(_console.ReadLine(), out value)
        {
            throw new ArgumentException("User input was not valid");
        }
    }
}

[Test]
public void TestGettingInput()
{
    var console = new TestableConsole("abc");

    var classObject = new TestClass(console);

    Assert.Throws<ArgumentException>(() => classObject.RunBusinessRules());
}

I would go with trying to avoid Unit Testing the Console and taking a dependency on it.

Upvotes: 10

Arghya C
Arghya C

Reputation: 10078

To unit test a code, that piece of code needs to be testable.

To unit test a method, what we do is pass in some known input and check it is returning expected result or if we are having correct side effect.

Now if you directly take your input inside your method, it is not very testable. It has two parts, reading the input and processing it. If the assert in your test fails, you don't know which step failed. Unit test should be aimed towards testing a unit or single responsibilty of a code.

To make it testable, read the input in main method and pass it to another method for processing (also common notion of object oriented programming), and test the second method with known parameters.

Upvotes: 0

Al W
Al W

Reputation: 7713

Just like the two comments posted. I would consider refactoring your code to look something more like

string input = Console.ReadLine();

try
{
    int PhoneIMEINumber = parse_input(input); 
}
catch(Exception)
{
    return null;
}

and then you'd have a function

public int parse_input(string input)
{
    return int.Parse(input);
}

THEN you'd write a unit test for the parse_input function. The example code seems quite trivial though and it is hard to justify writing a unit test around a wrapper function for int.Parse(), but I'm assuming that your parsing might get more complicated in the future.

Upvotes: 4

Lee
Lee

Reputation: 144136

You can set Console.In to a given text reader using SetIn:

var sr = new StringReader("Invalid int");
Console.SetIn(sr);

int? parsed = MethodUnderTest();
Assert.IsNull(parsed);

Upvotes: 9

Related Questions