# Saturday, October 01, 2011

Mocking von SerialDataReceivedEventArgs

Neulich stand ich vor dem Problem, dass ich eine Klasse mit Hilfe von Unit-Tests testen wollte, die einen seriellen Port verwendet. Zum einen implementiert System.IO.Ports.SerialPort kein Interface, welches zur Erstellung eines Mock-Objekts genutzt werden kann. Dies lies sich aber dadurch umgehen, dass ich ein entsprechendes Interface mit allen öffentlichen Membern von SerialPort manuell erstellt habe, um dieses Interface dann von einem Wrapper, der von SerialPort ableitet, implementieren zu lassen. Die zu testende Klasse nutzt nun den SerialPort nicht mehr direkt, sondern bekommt eine entsprechende Instanz, welches das Interface implementiert, per Konstruktorinjektion zur Verfügung gestellt. Somit ist es auch möglich, der Klasse ein Mock-Objekt zu injizieren.

Allerdings stand auch schon das nächste Problem parat. Die zu testende Klasse registriert sich auf das DataReceived-Event des SerialPorts. Wenn Daten empfangen werden, wird über die EventType-Eigenschaft der übergebenen SerialDataReceivedEventArgs ermittelt, ob es sich bei den empfangenen Daten um Nutzdaten oder das EOF-Zeichen handelt. Auf EOF reagiert die zu testende Klasse und liest den Empfangspuffer aus. Um dieses Verhalten testen zu können, muss der gemockte serielle Port also ein Event mit entsprechendem EventType werfen. SerialDataReceivedEventArgs stellt jedoch keine Konstruktorüberladung bereit, die einen Parameter vom Typ EventType akzeptiert. Auch ist der EventType einer Instanz nicht schreibbar. Somit ist es nicht ohne weiteres möglich, ein Testevent zu erzeugen.

Abhilfe schafft hier Reflection. Man holt sich zuerst die Informationen über den nicht öffentlichen Konstruktor, der den benötigten Parameter enthält, um ihn anschließend mit dem korrekten Wert aufzurufen:

ConstructorInfo constructor = typeof (SerialDataReceivedEventArgs).GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance,
    null,
    new[] {typeof (SerialData)},
    null);

SerialDataReceivedEventArgs eventArgs = 
    (SerialDataReceivedEventArgs)constructor.Invoke(new object[] {SerialData.Eof});

Geschwindigkeitsrekorde wird man mit dieser Art der Instanziierung sicher nicht brechen, aber für einen Unit-Test ist es ein gangbarer Weg.

Saturday, October 01, 2011 2:08:00 PM (W. Europe Daylight Time, UTC+02:00) #  Comments [0] | Trackback