KB Article T852605
Visible to All Users

How to unit test event handlers in Controllers

Test Scenario
We have the following Controller that subscribes to the NewObjectViewController ObjectCreated event. Our task is to check that NewObjectViewController exists and the right handler subscribes to the NewObjectViewController ObjectCreated event.

C#
public class CreateLinkedSaleBaseDescendantController : ViewController { protected override void OnActivated() { base.OnActivated(); NewObjectViewController standardController = Frame.GetController<NewObjectViewController>(); standardController.ObjectCreated += CreateLinkedSaleBaseDescendantController_ObjectCreated; } public void CreateLinkedSaleBaseDescendantController_ObjectCreated(object sender, ObjectCreatedEventArgs e) { // ... } }
Visual Basic
Public Class CreateLinkedSaleBaseDescendantController Inherits ViewController Protected Overrides Sub OnActivated() MyBase.OnActivated() Dim standardController = Frame.GetController(Of NewObjectViewController)() AddHandler standardController.ObjectCreated, AddressOf CreateLinkedSaleBaseDescendantController_ObjectCreated End Sub Public Sub CreateLinkedSaleBaseDescendantController_ObjectCreated(ByVal sender As Object, ByVal e As ObjectCreatedEventArgs) '... End Sub End Class

Unit Test Implementation
Let's create a test fixture class and a test method:

C#
using System; using System.Reflection; using DevExpress.ExpressApp; using DevExpress.ExpressApp.SystemModule; using NUnit.Framework; using MySolution.Module.Controllers; namespace MySolution.Tests { [TestFixture] public class CreateLinkedSaleBaseDescendantControllerTest { [Test] public void Test() { } }
Visual Basic
Imports System.Reflection Imports DevExpress.ExpressApp Imports DevExpress.ExpressApp.SystemModule Imports NUnit.Framework Imports MySolution.Module <TestFixture> Public Class CreateLinkedSaleBaseDescendantControllerTest <Test> Public Sub Test() End Sub End Class

Our code uses a Frame instance. Since XafApplication or TemplateContext are not used in our code, we can pass nulls to the Frame constructor.

C#
Frame frame = new Frame(null, null);
Visual Basic
Dim frame As Frame = New Frame(Nothing, Nothing)

Create CreateLinkedSaleBaseDescendantController and NewObjectViewController instances and register them in the Frame.

C#
NewObjectViewController newObjectViewController = new NewObjectViewController(); frame.RegisterController(newObjectViewController); CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController(); frame.RegisterController(controller);
Visual Basic
Dim newObjectViewController As NewObjectViewController = New NewObjectViewController() frame.RegisterController(newObjectViewController) Dim controller As CreateLinkedSaleBaseDescendantController = New CreateLinkedSaleBaseDescendantController() frame.RegisterController(controller)

Let's force activation of the controllers:

C#
controller.Active.Clear(); controller.Active["Test"] = true;
Visual Basic
controller.Active.Clear() controller.Active("Test") = True

Then, we need to check if CreateLinkedSaleBaseDescendantController subscribed to the NewObjectViewController ObjectCreated event. The Moq framework can mock events, but only if they are virtual or implemented in an interface. This is not our case. To verify this, we can use the following helper method that checks if our event handler exists as a method in the NewObjectViewController object.

C#
static void VerifyEventSubscription(object objectWithEvent, string eventName, object subscriber, string methodSignature) { var allBindings = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; var type = objectWithEvent.GetType(); var fieldInfo = type.GetField(eventName, allBindings); Assert.IsNotNull(fieldInfo); var eventHandler = fieldInfo.GetValue(objectWithEvent) as Delegate; Assert.IsNotNull(eventHandler); Assert.That(subscriber, Is.EqualTo(eventHandler.Target)); Assert.That(methodSignature, Is.EqualTo(eventHandler.Method.ToString())); }
Visual Basic
Private Shared Sub VerifyEventSubscription(ByVal objectWithEvent As Object, ByVal eventName As String, ByVal subscriber As Object, ByVal methodSignature As String) Dim allBindings = BindingFlags.IgnoreCase Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Static Dim type = objectWithEvent.GetType() Dim fieldInfo = type.GetField(eventName, allBindings) Assert.IsNotNull(fieldInfo) Dim eventHandler = TryCast(fieldInfo.GetValue(objectWithEvent), [Delegate]) Assert.IsNotNull(eventHandler) Assert.That(subscriber, [Is].EqualTo(eventHandler.Target)) Assert.That(methodSignature, [Is].EqualTo(eventHandler.Method.ToString())) End Sub

where

objectWithEvent - the object to which event we are subscribing,
eventName - the subscribed event's name,
subscriber - the object that subscribes to the event,
methodSignature - the event handler signature.

Let's call this method to complete our test:

C#
string targetMethodName = nameof(CreateLinkedSaleBaseDescendantController.CreateLinkedSaleBaseDescendantController_ObjectCreated); VerifyEventSubscription( newObjectViewController, nameof(newObjectViewController.ObjectCreated), controller, $"Void {targetMethodName}({typeof(Object)}, {typeof(ObjectCreatedEventArgs)})" );
Visual Basic
Dim targetMethodName As String = NameOf(CreateLinkedSaleBaseDescendantController.CreateLinkedSaleBaseDescendantController_ObjectCreated) VerifyEventSubscription( newObjectViewController, NameOf(newObjectViewController.ObjectCreated), controller, $"Void {targetMethodName}({GetType(Object)}, {GetType(ObjectCreatedEventArgs)})" )

The entire test code will be:

C#
[Test] public void Test() { Frame frame = new Frame(null, null); NewObjectViewController newObjectViewController = new NewObjectViewController(); frame.RegisterController(newObjectViewController); CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController(); frame.RegisterController(controller); controller.Active.Clear(); controller.Active["Test"] = true; string targetMethodName = nameof(CreateLinkedSaleBaseDescendantController.CreateLinkedSaleBaseDescendantController_ObjectCreated); VerifyEventSubscription( newObjectViewController, nameof(newObjectViewController.ObjectCreated), controller, $"Void {targetMethodName}({typeof(Object)}, {typeof(ObjectCreatedEventArgs)})" ); }
Visual Basic
<Test> Public Sub Test() Dim frame As Frame = New Frame(Nothing, Nothing) Dim newObjectViewController As NewObjectViewController = New NewObjectViewController() frame.RegisterController(newObjectViewController) Dim controller As CreateLinkedSaleBaseDescendantController = New CreateLinkedSaleBaseDescendantController() frame.RegisterController(controller) controller.Active.Clear() controller.Active("Test") = True Dim targetMethodName As String = NameOf(CreateLinkedSaleBaseDescendantController.CreateLinkedSaleBaseDescendantController_ObjectCreated) VerifyEventSubscription( newObjectViewController, NameOf(newObjectViewController.ObjectCreated), controller, $"Void {targetMethodName}({GetType(Object)}, {GetType(ObjectCreatedEventArgs)})" ) End Sub

See Also:
This series contains the following articles:

  1. How to write unit tests for XAF Actions, Controllers, and other custom UI logic
  2. How to unit test Action's enabled/disabled state based on user permissions
  3. How to unit test whether object property changes via Actions were successfully committed
  4. How to unit test object queries by criteria and Detail View creation
  5. How to unit test event handlers in Controllers (current)
  6. How to unit test New Action's custom business logic based on parent and nested Views
  7. How to unit test Action's enabled/disabled state based on target criteria and selection dependency types in List View
  8. How to unit test Action's enabled/disabled state based on target criteria and selection dependency types in Detail View
  9. How to unit test localized strings from CaptionHelper (Approach 1)
  10. How to unit test localized strings from CaptionHelper (Approach 2)
  11. How to unit test custom logic in XAF/XPO business classes

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.