2009-08-21 11 views
2

In einer meiner Anwendungen habe ich eine Klasse, die für Benutzereingaben zuständig ist. Die Standardmethode für die Eingabe ist die Konsole (Tastatur), und ich möchte einige Komponententests schreiben, um sicherzustellen, dass sie korrekt sind.Wie teste ich eine Konsolen-Eingabeklasse?

Ich erwäge, das google-test Framework für meine Unit-Tests zu verwenden, was es einfach macht, alle Tests zu automatisieren. Ich bin mir jedoch nicht sicher, wie ich das Testen der Konsoleneingabe automatisieren kann.

Gibt es eine Möglichkeit, Benutzereingaben auf der Tastatur zu simulieren? Oder muss ich meine Testeingabe manuell eingeben? Oder vielleicht umleiten Sie stdin (entweder in Code oder durch eine Rohrleitung, wenn Sie den Komponententest ausführen)?

EDIT: Ich plane mit GNU Readline für Benutzereingaben. Im Moment kann ich keine Möglichkeit sehen, den Eingangsstrom dieser Bibliothek umzuleiten - vielleicht hat jemand anderes damit Erfahrung?

+0

Was mit spöttischer stdin mit einem dateiähnliche Mockobjekt falsch? –

+0

Ich bin neu im Komponententest, also habe ich keine Mock-Objekte zuvor gesehen. Schau es dir jetzt an. –

Antwort

2

Sie könnten expect verwenden.

+0

Ich habe vor einem Jahrzehnt ein ETL-Konsolen-Tool mit einem Rauchtest getestet. Hat super funktioniert. – sal

+0

Könnte. Aber dann würden dogmatische Leute nicht mehr Ihre Test Unit Tests nennen, also Downvoting. Aber ja, erwarten Sie ist ein ausgezeichnetes Werkzeug und es lohnt sich, es auszuprobieren. – rasjani

+0

Okay, vielleicht werden sie nicht mehr Unit-Tests genannt, aber dies scheint der beste Weg zu sein zu testen, dass meine Input-Klasse so funktioniert, wie ich es möchte - ich kann sicherstellen, dass sie Eingaben mit Readline akzeptiert, während ich das nicht mit Spott machen konnte (soweit ich das sehen kann), müsste ich eine falsche Version von readline erstellen, was das Gegenteil von dem ist, was ich tun möchte. –

1

Mock den Eingang.

3

Grundsätzlich sollte Ihre Klasse in der Lage sein, einen zufälligen Eingabestream zu verwenden, nicht nur stdin (und Sie müssen es umgestalten, wenn es noch nicht möglich ist).

Danach können Sie einfach einen Mock-Stream mit Ihren benutzerdefinierten Daten einfügen und alle Benutzereingaben simulieren.

+0

Ich kann den Eingabestream nicht ändern, weil ich GNU readline verwende - siehe Bearbeiten zur Frage. –

+1

^Wenn Sie hart sind, eine Eingabe an die Gnu Readline Verdrahtung dann testen Sie Readline + Ihren Code. Auch hier sollten Sie Ihren Eingabehandhabungscode umgestalten, so dass Sie den readline Teil verspotten können. – rasjani

+0

Und ein weiterer Kommentar, dies ist einer der Gründe warum ich TDD am liebsten verwende, es zwingt Sie tatsächlich einen Code zu schreiben und zu entwerfen getestet werden. – rasjani

0

Für .NET/C# können Sie die Optionsklasse oder Variationen verwenden, die in dieser Frage gefunden werden. Weil Sie haben alle Befehle an die Delegierten abgebildet, können Sie dann Unit-Test am Ende der Delegierten der Methoden jeder und leicht unbekannte Befehle finden:

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)) 

Die MyHandler Klasse wäre ähnlich zu:

public class MyHandler 
{ 
    public void Show() { } 
    public void Connect(string[] args){} 
    public void Dir() {} 
} 
1

Ich plane die Verwendung von GNU Readline für Benutzereingaben. Im Moment kann ich nicht jede mögliche Weise, finden Sie in der Eingabe Strom dieser Bibliothek

Eine abstrakte Klasse mit Mitgliedern umzuleiten, die die Readline- Funktionalität entsprechen, die Sie verwenden möchten. Programmieren Sie gegen diese abstrakte Klasse statt direkt gegen die readline API. Verwenden Sie dependency injection, um eine Instanz dieser Klasse an den Code zu senden, der sie benötigt.

Dann können Sie zwei Implementierungen dieser Klasse erstellen: eine, die die readline-Bibliothek einfach umschließt, und eine weitere Scheinimplementierung, die Sie in Ihren Komponententests verwenden können. Die Scheinimplementierung würde zusätzliche Mitglieder haben, die es einfach machen, einen Benutzer zu simulieren.

0

Für die Konsole, ich wickle es immer nur mit meiner eigenen Implementierung.

Die Verwendung eines Wrappers und einer Schnittstelle für alle Steuerelemente von Drittanbietern, die an Komponententests beteiligt sind, macht die Arbeit mit einem Isolationsframework (wie Rhino Mocks) sehr einfach. Es gibt mir die Kontrolle über Tests und definiert explizit die Abhängigkeiten in meinem Code. Da ich neue Funktionen der Konsole brauche, kann ich sie einfach zur Wrapper-Oberfläche hinzufügen. Ich hatte noch keine Probleme mit der Schnittstelle aufgebläht ...

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(); 
    } 
} 

Hier ist ein Test in Aktion

[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(); 
} 
Verwandte Themen