Unit Testing Input & Output

Creating An Attribute for Redirecting Console Input & Output

While writing my Essential C# (Addison-Wesley), I created many console applications.  Unit testing has been rather cumbersome.  I decided to create an attribute that can redirect the console input and output so that I can supply various inputs and then test the output.

I went with MbUnit because of its ability to support custom decorators.  Although MbUnit does support ConsoleLikeFixtures and ConsoleTesting out of the box, I was looking to use decorators as follows:

Test expected output

[Test] 
[ConsoleOutputExpected( "Test" )]  
public void Basic() 
{ 
    Console.Write( "Test" ); 
}

Provide automated input

[Test] 
[ConsoleInput( "Test" )]  
public void Basic() 
{  
    string input = Console.ReadLine(); 
    Assert.AreEqual( "Test" , input); 
}

In addition, I wanted ConsoleOutputExpected to support wild-cards, regular expressions, and straight string comparison.

Use regular expressions in the expected console output

[Test] 
[ConsoleOutputExpected( "^Test" , SearchTypeOptions.UseRegularExpressions)]  
public void StringBeginsWith() 
{ 
    Console.Write( "Test5" ); 
}

Use wild-cards in the expected console output

[Test] 
[ConsoleOutputExpected( @"Test* Test*" , SearchTypeOptions.UseWildcards)]  
public void MultiLineEndsWith() 
{ 
    Console.WriteLine( "Test1" ); 
    Console.WriteLine( "Test2" ); 
}

Anyway, following the instructions I derived from  MbUnit.Core.Framework.DecoratorPatternAttribute and implemented the Execute() method as follows:

 public override Object Execute(Object o, IList args) 
 {  
     using  (MemoryStream stream = new MemoryStream())  
     using  (TextWriter writer = new StreamWriter(stream))  
     using  (TextReader reader = new StreamReader(stream)) 
     { 
         ((StreamWriter)writer).AutoFlush = true ;
        TextReader originalReader = Console.In;
        try  
        {  
            object result;
            Console.SetIn(reader);
            writer.Write(Attribute.Input);
            stream.Seek(0, SeekOrigin.Begin); 
            result = this.Invoker.Execute(o, args);  
            return null; 
        }  
        finally  
        { 
            Console.SetIn(originalReader); 
        } 
    } 
}

In summary, the process was relatively simple, and I was pleased with the results, with two exceptions:

  1. The C# string literal syntax is funky when text spans lines.  The problem is that code is indented, but the text itself can’t be because it is taken literally.  This makes for rather peculiar code.  I wish there was an elegant way to keep indentation within the source code but not the literal.
  2. The new Console methods in .NET 2.0 like System.Console.CursorTop/Left and System.Console.SetCursorPosition() cannot be redirected.  It always throws an invalid handle exception.  This makes unit testing these methods beyond reasonable.  I posted a bug about this problem in the hopes that Microsoft would fix the issue before release.

Download the source code.

Tags:

2 thoughts on “Unit Testing Input & Output”

Leave a Reply

Your email address will not be published. Required fields are marked *