tags:

views:

133

answers:

2

Im starting a process and redirecting it error stream to be able to parse it and know what happened. Im doing it in such a way:

_proc.ErrorDataReceived += new DataReceivedEventHandler(NetErrorDataHandler);

where NetErrorDataHandler have the following signature:

private void NetErrorDataHandler(object sendingProcess, DataReceivedEventArgs errLine)

So far i have to work with DataReceivedEventArgs. But how to test it, except for running the process Im working with? You cant create instance of DataReceivedEventArgs with data you like. so how to overcome it? The only way I see now is create a process that would do the work for me. But it`s not the best way for testing.

+1  A: 

What data does your application use out of DataReceivedEventArgs?

Your best solution may be to wrap DataReceivedEventArgs with a class that you are able to inject data into for testing and then pass that to an implementation method instead of doing all of your work inside of the current event handler.

Let me show you. First, create your custom wrapper class:

internal class CustomDataReceivedEventArgs : EventArgs
{

    public string Data { get; set; }


    public CustomDataReceivedEventArgs(string _Data) 
    {
        Data = Data;
    }

    public CustomDataReceivedEventArgs(DataReceivedEventArgs Source) 
    {
        Data = Source.Data;
    }
}

Next, move your event handler impelmentation into the new method:

    private void NetErrorDataHandler_Implementation(object sendingProcess, 
        CustomDataReceivedEventArgs errLine) 
    {
        //Your actual event handling implementation here...
    }

Finally, set your event handler to do nothing but call the new method:

    private void NetErrorDataHandler(object sendingProcess, 
        DataReceivedEventArgs errLine)
    {
        NetErrorDataHandler_Implementation(sendingProcess, 
            new CustomDataReceivedEventArgs(errLine));
    }

Now, you can test your implementation by calling your implementation like this:

        NetErrorDataHandler_Implementation(new object(),
            new CustomDataReceivedEventArgs("My Test Data"));
Robert Venables
Sorry, wrap with class. How is that? Actually my class is subscribing for DataReceivedEventHandler as you can see from the code. So it`s expecting concrete method signature to subscribe with. Even if datareceivedEventArgs is wrapped how can I use it if it`s always empty as it`s data property is readonly?
Yaroslav Yakovlev
If there`ll be no other ideas I`ll mark it as solution. But I don`t like it as to test you`ll need to add special method signature that will work with CustomDataReceivedEventArgs to provide some data. But way to work with data is public interface. I don`t want to change it for testing purposes. So it will test my implementation but I need to create a special method for that and make it public.
Yaroslav Yakovlev
I understand, this was just a quick and dirty way of getting around the unsettable data problem while making very few changes to your existing classes.
Robert Venables
I don`t think there`s any other good solution. So thank you, I think yours is quite acceptable for testing purposes. What I did - just separated notifier from notifieble. Now notifier just get`s subscribes to process event and when he get`s it - notifies his curious object :-). And I can test how that curious object handles any data he receives as it`s ok for him to receive plain string.
Yaroslav Yakovlev
That sounds like a fine solution - good use of the observer pattern.
Robert Venables
A: 

I just found out that it is possible to create objects with internal constructors and set the internal fields. Obviously, this is discouraged but it would work for short term mocking. Here's an example:

    private DataReceivedEventArgs CreateMockDataReceivedEventArgs(string TestData)
    {

        if (String.IsNullOrEmpty(TestData))
            throw new ArgumentException("Data is null or empty.", "Data");

        DataReceivedEventArgs MockEventArgs =
            (DataReceivedEventArgs)System.Runtime.Serialization.FormatterServices
             .GetUninitializedObject(typeof(DataReceivedEventArgs));

        FieldInfo[] EventFields = typeof(DataReceivedEventArgs)
            .GetFields(
                BindingFlags.NonPublic |
                BindingFlags.Instance |
                BindingFlags.DeclaredOnly);

        if (EventFields.Count() > 0)
        {
            EventFields[0].SetValue(MockEventArgs, TestData);
        }
        else
        {
            throw new ApplicationException(
                "Failed to find _data field!");
        }

        return MockEventArgs;

    }

Use as such:

DataReceivedEventArgs TestEventArgs = CreateMockDataReceivedEventArgs("Test");

...this took a bit of research, I didn't think it was possible :-)

Robert Venables