views:

606

answers:

7

In one of my applications I have a class which is responsible for user input. The default method of input is the console (keyboard), and I want to write some unit tests for it to make sure it is correct.

I am considering using the google-test framework for my unit testing, which makes it easy to automate all the testing. However, I am not sure how I can automate testing of the console input.

Is there any way to simulate user input on the keyboard? Or do I have to manually enter my test input? Or perhaps redirect stdin (either in code or by a pipe when running the unit test)?

EDIT: I am planning on using GNU readline for user input. At the moment I can't see any way to redirect the input stream of this library - perhaps someone else has experience with this?

+1  A: 

Mock the input.

S.Lott
+1  A: 

You could use expect.

chotchki
I used expect to smoke-test a console ETL tool a decade ago. Worked great.
sal
Could. But then dogmatic people wouldn't call your tests unit tests anymore, so downvoting. But yes, expect is excellent tool and its worth to check out.
rasjani
Okay, maybe they aren't called unit-tests any more, but this does seem to be the best way to test that my input class is working the way I want it to - I can make sure that it accepts input using readline, whereas I couldn't do that using mocking (as far as I can see) - I would have to create a fake version of readline, which is the opposite of what I want to do.
a_m0d
+3  A: 

Basically, your class should be able to use random input stream, not only stdin (and you have to refactor it, if it's unable yet).

After that you'll be able to simply put a mock stream with your custom data and to simulate any user input.

Vanya
I can't change the input stream because I am using GNU readline - see edit to question.
a_m0d
^ if you are hard wiring a input to the gnu readline then you are testing readline + your code. Again, you should refactor your input handling code so that you can mock the readline part..
rasjani
And another comment, this is one of the reason why, i prefer to use TDD, it forces you to actually write and design a code that can be tested.
rasjani
No matter, what library are you using, your app should have some kind of <b>dispatch_input_from_outer_world(string inputMessage)</b> method in your code. Basically, this is the point, which you should attach the mock to :-)
Vanya
The class interface allows multiple implementations, which will use different input mechanisms, but I need to test that the one using `readline` works as I want it to. So I can't change the input handling code in this implementation, since then I would no longer be testing that the implementation works as desired.
a_m0d
A: 

If your platform is .NET, here's one way to do it.

Mark Seemann
A: 

For .NET/C# you could use the Options class or variations found in this question. Because you have all commands mapped to delegates, you can then unit test each of the methods at the end of the delegates, and easily find unknown commands:

MyHandler handler = new MyHandler()
CommandOptions options = new CommandOptions();

// Put this in the test Setup
options.Add("show", handler.Show)
        .Add("connect", v => handler.Connect(v))
        .Add("dir", handler.Dir);

if (!options.Parse(args))
   Assert.Fail(string.Format("{0} was not recognised.",args))

The MyHandler class would be similar to:

public class MyHandler
{
    public void Show() { }
    public void Connect(string[] args){}
    public void Dir() {}
}
Chris S
A: 

I am planning on using GNU readline for user input. At the moment I can't see any way to redirect the input stream of this library

Create an abstract class with members that match the readline functionality that you want to use. Program against this abstract class instead of directly against the readline API. Use dependency injection to get an instance of this class to the code that needs it.

Then you can create two implementations of this class: one which simply wraps the readline library, and another mock implementation which you can use in your unit tests. The mock implementation would have extra members that make it easy to simulate a user.

Wim Coenen
A: 

For the console, I always just wrap it with my own implementation.

Using a wrapper and interface for all 3rd party controls that are involved in unit tests makes working with an isolation framework (like Rhino Mocks) super easy. It gives me control over testing, and explicitly defines the dependancies in my code. As I need new features of the console I can just add them to the wrappers interface. I have not had issues with interface bloat yet...

public interface IConsoleShim
{
    void WriteLine(string message);
    ConsoleKeyInfo ReadKey();
}
public class ConsoleShim : IConsoleShim
{
    public void WriteLine(string message)
    {
        Console.WriteLine(message);
    }
    public ConsoleKeyInfo ReadKey()
    {
        return Console.ReadKey();
    }
}

Here is a test in action

[NUnit.Framework.Test]
public void Run_writes_to_console_100_times_waits_for_keypress()
{
    // arrange
    Rhino.Mocks.MockRepository mocks = new Rhino.Mocks.MockRepository();
    IConsoleShim consoleMock = mocks.StrictMock<IConsoleShim>();
    Program program = new Program(consoleMock);
    int expected = 100;

    // VerifyAll automatically called
    Rhino.Mocks.With.Mocks(mocks).Expecting(() =>
        {
            Rhino.Mocks.Expect.Call(() => consoleMock.WriteLine("")).IgnoreArguments().Repeat.Times(expected);
            Rhino.Mocks.Expect.Call(consoleMock.ReadKey()).Return(new ConsoleKeyInfo());
        });

    //act
    program.Run();
}
MarcLawrence