通用事件测试方法,防止代码复制

问题描述:

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

我的第一次迭代是:

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来执行。

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

我觉得uglyness已经是相当明显的(突然我需要关心如何计数的事件,执行完成):

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

     } 
    } 

现在的代码将被调用。但更重要的是,上面的代码将无法工作。

代替“更改”EventFireTest中的变量,lambda创建变量的本地副本,并更新该变量。所以我需要通过引用来传递整数,如果没有一些复杂的方法,这也是不可能的。

这些问题相结合使我相信我目前还没有找到解决这个问题的正确方向。最直接的方式是什么?

而不是让你的委托取一个整数并递增它,而是让委托提供一个事件处理程序,并让委托的主体将该事件处理程序添加到事件中。这使得来电者:

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

对于你现在只需要把你在原来的版本写的处理程序,并将其作为参数传递给该操作的测试方法。

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

+0

这会引发错误:'不能将类型'System.EventHandler'隐式转换为'System.ComponentModel.PropertyChangedEventHandler''。现在我希望代码能够实际处理“自定义事件”以及“内置事件”。所以限制'T'是我其实不想做的事情。 – paul23

+0

@ paul23如果你想解决* any *类型的事件的问题,而不仅仅是一个特定类型的委托,那么你的问题是一样的[这个问题](https://stackoverflow.com/questions/12865848/general-purpose-fromevent-method),这并不是你想要的东西,但如果这就是你必须做的,那就是你必须做的。 – Servy

+0

那么问题是,我有一个'PropertyChanged','PropertyChanging','DataChanged','DataChanging'和基于集合的事件,我都需要“测试”几种不同类型的对象。 - 代码重复是否真的是这种“标准”方式? (对于我的技能组合来说,链接的答案对于目前来说确实“太难了”,使其不适合作为测试工具)。 – paul23