【问题标题】:Generic event test method, preventing code duplication通用事件测试方法,防止代码重复
【发布时间】:2017-10-20 15:28:45
【问题描述】:

我尝试创建一个测试套件来测试包含某个事件的类。基本上我希望调用一个方法,给该方法一个事件,一段测试代码和一个执行事件的对象,最后是引发事件的预期次数。

我的第一次迭代是:

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

        }
    }

它会被称为:

EventFireTest(obj.PropertyChanged, obj, action);

其中动作包含动作对以及一些元数据,例如正在执行的动作的视觉线索。

但是,当我尝试将事件传递给此函数时,问题是“事件只能是 += 和 -= 的 lhs。尽管那是我最终对这次活动所做的唯一事情。 (编译器理论上应该可以检查这个?)

现在为了规避这个问题,我采用了一种更臃肿的方法,直接引入了已经不需要的代码重复以及将内部细节泄露给外部测试方法。通过为要执行的EventFireTest 方法提供一个lambda。

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

        }
    }

现在代码将被称为:

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

我认为丑陋已经很明显了(突然间我需要关心如何计算事件执行次数)。但更重要的是,上面的代码不起作用。

lambda 不会“更改”EventFireTest 中的变量,而是创建变量的本地副本,并对其进行更新。所以我需要通过引用传递整数,如果没有一些复杂的方式,这也是不可能的。

这些问题加在一起让我相信我目前没有走上解决这个问题的正确轨道。有最直接的方法吗?

【问题讨论】:

    标签: c# unit-testing events lambda


    【解决方案1】:

    与其让您的委托接受一个整数并递增它,不如让委托提供一个事件处理程序并让委托的主体将该事件处理程序添加到事件中。这使得调用者:

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

    对于测试方法,您现在只需要获取您在原始版本中编写的处理程序并将其作为参数传递给该操作。

    解决此问题的另一种方法是将T 限制为实现某些接口的类型,其中接口定义了您需要订阅的事件。仅当此测试的所有调用者都在逻辑上测试特定类型的事件而不是某些任意事件时,这才是合适的。您的代码看起来更像后者,因此这似乎不适合您的情况。

    【讨论】:

    • 这会引发错误:Cannot implicitly convert type 'System.EventHandler' to 'System.ComponentModel.PropertyChangedEventHandler'。现在我希望代码能够实际使用“自定义事件”以及“内置事件”。所以约束T 是我实际上不想做的事情。
    • @paul23 如果您希望解决任何类型事件的问题,而不仅仅是特定类型的委托,那么您的问题与this problem相同,这并不是你真正想去的地方,但如果这是你必须要做的,那就是你必须要做的。
    • 问题是,我有一个PropertyChangedPropertyChangingDataChangedDataChanging 以及我都“需要测试”的基于集合的事件,用于几种不同类型的对象。 - 那么代码复制真的是“标准”方式吗? (链接的答案对于我目前的技能来说确实“太难”了,因此它不是作为测试工具的好选择)。
    • @paul23 您的选择是为您想要支持的每种类型的事件委托设置不同的重载(在附加事件处理程序后,它们都可以为大多数实现调用私有方法)或使用我链接到的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2020-04-24
    • 2017-09-25
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多