-1

Well I tried to create a test-suite to test classes which contain a certain event. Basically I wish to call a method, give that method an event, a piece of test code and an object on which the event executes, and finally the expected amount of times the event is raised.

My first iteration is:

    protected void EventFireTest<T>(EventHandler theEvent, T g, TestAction<T> action) where T: class{
        var invokeChangedCount = 0;

        theEvent += (sender, e) => { ++invokeChangedCount; };            

        foreach (var actionPair in action.AllActions) {
            invokeChangedCount = 0;
            var num = actionPair.Item2;
            var actualAction = actionPair.Item1;
            actualAction(g);

            Assert.That(actual: invokeChangedCount, expression: Is.EqualTo(expected: num));

        }
    }

It would be called like:

EventFireTest(obj.PropertyChanged, obj, action);

Where action contained the action-pair as well as some meta data like the a visual clue of the action being performed.

However when I tried to pass an event to this function, the problem was that "events can only be the lhs for += and -=". Even though that is the only thing I ultimately did with the event. (Compiler should be able to check this theoretically?)

Now to circumvent this problem I went for a more bloated approach, directly introducing already unwanted code duplication as well as bleeding internal details to the outer test method. By providing a lambda for the EventFireTest method to execute.

    protected void EventFireTest<T>(Action<int> EventSetupAction, T g, TestAction<T> action) where T: class{
        var invokeChangedCount = 0;

        EventSetupAction(invokeChangedCount);

        foreach (var actionPair in action.AllActions) {
            invokeChangedCount = 0;
            var num = actionPair.Item2;
            var actualAction = actionPair.Item1;
            actualAction(g);

            Assert.That(actual: invokeChangedCount, expression: Is.EqualTo(expected: num));

        }
    }

Now the code would be called as:

        Action<int> setup = n => obj.PropertyChanged += (sender, e) => {
            ++n;
        };
        EventFireTest(setup, obj, action);

I think the uglyness is already quite visible (suddenly I need to care how counting the event executions is done). But more important that above code won't work.

Instead of "changing" the variable in EventFireTest the lambda creates a local copy of the variable, and updates that. So I would need to pass the integer by reference, which is again not possible without some convoluted way.

These problems combined lead me to believe I am currently not on the right track to solve this. There most be a more straightforward way?

paul23
  • 8,799
  • 12
  • 66
  • 149

1 Answers1

-1

Rather than having your delegate take an integer and increment it, instead have the delegate provide an event handler and have the body of the delegate be adding that event handler to the event. This makes the caller:

Action<EventHandler> setup = handler => obj.PropertyChanged += handler;
EventFireTest(setup, obj, action);

For the test method you now just need to take the handler you wrote in your original revision and pass it as the parameter to that action.

Another way around the problem would be to constrain T to be types that implement some interface, where the interface defines the event that you need to subscribe. This would only be appropriate if all of the callers of this test are logically testing a particular type of event, rather than some arbitrary event. Your code looks more like the latter, so that doesn't appear to be appropriate for your situation.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • This raises the error: `Cannot implicitly convert type 'System.EventHandler' to 'System.ComponentModel.PropertyChangedEventHandler'`. Now I wish the code to actually work with "custom events" as well as the "built in events". So constraining `T` is something I actually do not wish to do. – paul23 Oct 20 '17 at 15:47
  • @paul23 If you wish to solve the problem for *any* type of event, rather than just a specific type of delegate, then you problem is the same as [this problem](https://stackoverflow.com/questions/12865848/general-purpose-fromevent-method), which isn't really where you want to be, but if that's what you have to do, then that's what you have to do. – Servy Oct 20 '17 at 15:49
  • Well the problem is, I have a `PropertyChanged`, `PropertyChanging`, `DataChanged`, `DataChanging` and the collection based events I all "would need to test", for several different types of objects. - Is code duplication really the "standard" way for this then? (The linked answer really is "too difficult" for my skillset at the moment making it not a good option as a test-tool). – paul23 Oct 20 '17 at 15:56
  • @paul23 Your options are either to have a different overload for each type of event delegate you want to support (they could all call a private method for most of the implementation, after attaching the event handler) or to use a solution like one that I linked to. – Servy Oct 20 '17 at 16:00