KB Article T851879
Visible to All Users

How to unit test whether object property changes via Actions were successfully committed

Test Scenario
We have a Controller that changes the Priority or Status property of selected objects when a user invokes an Action. Our task is to test that the Priority or Status property was changed and that the changes are saved, i.e. the IObjectSpace.CommitChanges method was called.

C#
public class TaskActionsController : ViewController { private SingleChoiceAction setTaskAction; public ChoiceActionItem setPriorityItem; public ChoiceActionItem setStatusItem; public TaskActionsController() setTaskAction = new SingleChoiceAction(this, "SetTaskAction", PredefinedCategory.Edit); setPriorityItem = new ChoiceActionItem(CaptionHelper.GetMemberCaption(typeof(DemoTask), nameof(DemoTask.Priority)), null); setTaskAction.Items.Add(setPriorityItem); FillItemWithEnumValues(setPriorityItem, typeof(Priority)); setStatusItem = new ChoiceActionItem(CaptionHelper.GetMemberCaption(typeof(DemoTask), nameof(DemoTask.Status)), null); setTaskAction.Items.Add(setStatusItem); FillItemWithEnumValues(setStatusItem, typeof(TaskStatus)); setTaskAction.Execute += SetTaskAction_Execute; } private void FillItemWithEnumValues(ChoiceActionItem parentItem, Type enumType) { EnumDescriptor ed = new EnumDescriptor(enumType); foreach (object current in Enum.GetValues(enumType)) { ChoiceActionItem item = new ChoiceActionItem(ed.GetCaption(current), current); item.ImageName = ImageLoader.Instance.GetEnumValueImageName(current); parentItem.Items.Add(item); } } private void SetTaskAction_Execute(object sender, SingleChoiceActionExecuteEventArgs args) { IObjectSpace objectSpace = View is ListView ? Application.CreateObjectSpace(typeof(DemoTask)) : View.ObjectSpace; bool shouldCommitChanges = View is ListView || ((View as DetailView)?.ViewEditMode == ViewEditMode.View); SetupDemoTaskProperties(args.SelectedObjects, args.SelectedChoiceActionItem, objectSpace, shouldCommitChanges); if (View is ListView) { View.ObjectSpace.Refresh(); } } public void SetupDemoTaskProperties(IList selectedObjects, ChoiceActionItem selectedChoiceActionItem, IObjectSpace objectSpace, bool shouldCommitChanges) { foreach (object obj in selectedObjects) { DemoTask objInNewObjectSpace = (DemoTask)objectSpace.GetObject(obj); if (selectedChoiceActionItem.ParentItem == setPriorityItem) { objInNewObjectSpace.Priority = (Priority)selectedChoiceActionItem.Data; } else if (selectedChoiceActionItem.ParentItem == setStatusItem) { objInNewObjectSpace.Status = (TaskStatus)selectedChoiceActionItem.Data; } } if (shouldCommitChanges) { objectSpace.CommitChanges(); } } }
Visual Basic
Public Class TaskActionsController Inherits ViewController Private _setTaskAction As SingleChoiceAction Public setPriorityItem As ChoiceActionItem Public setStatusItem As ChoiceActionItem Public Sub New() _setTaskAction = New SingleChoiceAction(Me, "SetTaskAction", PredefinedCategory.Edit) setPriorityItem = New ChoiceActionItem(CaptionHelper.GetMemberCaption(GetType(DemoTask), NameOf(DemoTask.Priority)), Nothing) _setTaskAction.Items.Add(setPriorityItem) FillItemWithEnumValues(setPriorityItem, GetType(Priority)) setStatusItem = New ChoiceActionItem(CaptionHelper.GetMemberCaption(GetType(DemoTask), NameOf(DemoTask.Status)), Nothing) _setTaskAction.Items.Add(setStatusItem) FillItemWithEnumValues(setStatusItem, GetType(TaskStatus)) AddHandler _setTaskAction.Execute, AddressOf SetTaskAction_Execute End Sub Public ReadOnly Property SetTaskAction() As SingleChoiceAction Get Return _setTaskAction End Get End Property Private Sub FillItemWithEnumValues(ByVal parentItem As ChoiceActionItem, ByVal enumType As Type) Dim ed As New EnumDescriptor(enumType) For Each current As Object In System.Enum.GetValues(enumType) Dim item As New ChoiceActionItem(ed.GetCaption(current), current) item.ImageName = ImageLoader.Instance.GetEnumValueImageName(current) parentItem.Items.Add(item) Next current End Sub Private Sub SetTaskAction_Execute(ByVal sender As Object, ByVal args As SingleChoiceActionExecuteEventArgs) Dim objectSpace As IObjectSpace = If(TypeOf View Is ListView, Application.CreateObjectSpace(GetType(DemoTask)), View.ObjectSpace) Dim shouldCommitChanges As Boolean = TypeOf View Is ListView OrElse (TryCast(View, DetailView)?.ViewEditMode = ViewEditMode.View) SetupDemoTaskProperties(args.SelectedObjects, args.SelectedChoiceActionItem, objectSpace, shouldCommitChanges) If TypeOf View Is ListView Then View.ObjectSpace.Refresh() End If End Sub Public Sub SetupDemoTaskProperties(ByVal selectedObjects As IList, ByVal selectedChoiceActionItem As ChoiceActionItem, ByVal objectSpace As IObjectSpace, ByVal shouldCommitChanges As Boolean) For Each obj As Object In selectedObjects Dim objInNewObjectSpace As DemoTask = CType(objectSpace.GetObject(obj), DemoTask) If selectedChoiceActionItem.ParentItem Is setPriorityItem Then objInNewObjectSpace.Priority = CType(selectedChoiceActionItem.Data, Priority) ElseIf selectedChoiceActionItem.ParentItem Is setStatusItem Then objInNewObjectSpace.Status = CType(selectedChoiceActionItem.Data, TaskStatus) End If Next If shouldCommitChanges Then objectSpace.CommitChanges() End If End Sub End Class

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

C#
using DevExpress.ExpressApp; using DevExpress.Xpo; using MySolution.Module.BusinessObjects; using MySolution.Module.Controllers; using Moq; using NUnit.Framework; using System.Linq; namespace MySolution.Tests { [TestFixture] public class SetTaskActionExecuteTest { [Test] public void SetTaskActionSetPriorityTest() { } } }
Visual Basic
Imports DevExpress.ExpressApp Imports DevExpress.Xpo Imports MainDemo.Module.BusinessObjects Imports MainDemo.Module.Controllers Imports Moq Imports NUnit.Framework <TestFixture> Public Class SetTaskActionExecuteTest <Test> Public Sub SetTaskActionSetPriorityTest() End Sub End Class

First, we need a list of selected objects. Let's create it in our test method:

C#
DemoTask[] demoTasks = { new DemoTask(Session.DefaultSession), new DemoTask(Session.DefaultSession) };
Visual Basic
Dim demoTasks As DemoTask() = {New DemoTask(Session.DefaultSession), New DemoTask(Session.DefaultSession)}

Then, create a TaskActionsController instance and obtain a ChoiceActionItem from it. We will assume that a user clicks this ChoiceActionItem.

C#
TaskActionsController controller = new TaskActionsController(); const Priority priority = Priority.High; var selectedChoiceActionItem = controller.setPriorityItem.Items.Find(priority);
Visual Basic
Dim controller As TaskActionsController = New TaskActionsController() Const priority As Priority = Priority.High Dim selectedChoiceActionItem = controller.setPriorityItem.Items.Find(priority)

All our business logic is implemented in the TaskActionsController SetupDemoTaskProperties method. We need to test only this method. This is XAF responsibility to raise the action Execute event. We don't need to unit test this logic. Use functional tests instead.

The SetupDemoTaskProperties method uses the IObjectSpace GetObject method. Let's mock it:

C#
var objectSpaceMock = new Mock<IObjectSpace>(); objectSpaceMock.Setup(o => o.GetObject(It.IsAny<object>())).Returns<object>(obj => obj);
Visual Basic
Dim objectSpaceMock = New Mock(Of IObjectSpace)() objectSpaceMock.Setup(Function(o) o.GetObject(It.IsAny(Of Object)())).Returns(Function(obj) obj)

When the IObjectSpace GetObject method is called with an object, we just return this object. That's all and now, we can call the tested SetupDemoTaskProperties method.

C#
controller.SetupDemoTaskProperties(demoTasks, selectedChoiceActionItem, objectSpaceMock.Object, true);
Visual Basic
controller.SetupDemoTaskProperties(demoTasks, selectedChoiceActionItem, objectSpaceMock.Object, True)

Now, make an assertion that all DemoTask objects changed their Priority property and verify that the IObjectSpace CommitChanges method was called:

C#
Assert.That(demoTasks.Select(dt => dt.Priority), Is.All.EqualTo(priority)); objectSpaceMock.Verify(o => o.CommitChanges());
Visual Basic
Assert.That(demoTasks.Select(Function(dt) dt.Priority), [Is].All.EqualTo(priority)) objectSpaceMock.Verify(Sub(o) o.CommitChanges())

That's it. The full test method code is the following:

C#
[Test] public void SetTaskActionSetPriorityTest() { DemoTask[] demoTasks = { new DemoTask(Session.DefaultSession), new DemoTask(Session.DefaultSession) }; TaskActionsController controller = new TaskActionsController(); const Priority priority = Priority.High; var selectedChoiceActionItem = controller.setPriorityItem.Items.Find(priority); var objectSpaceMock = new Mock<IObjectSpace>(); objectSpaceMock.Setup(o => o.GetObject(It.IsAny<object>())).Returns<object>(obj => obj); controller.SetupDemoTaskProperties(demoTasks, selectedChoiceActionItem, objectSpaceMock.Object, true); Assert.That(demoTasks.Select(dt => dt.Priority), Is.All.EqualTo(priority)); objectSpaceMock.Verify(o => o.CommitChanges()); }
Visual Basic
<Test> Public Sub SetTaskActionSetPriorityTest() Dim demoTasks As DemoTask() = {New DemoTask(Session.DefaultSession), New DemoTask(Session.DefaultSession)} Dim controller As TaskActionsController = New TaskActionsController() Const priority As Priority = Priority.High Dim selectedChoiceActionItem = controller.setPriorityItem.Items.Find(priority) Dim objectSpaceMock = New Mock(Of IObjectSpace)() objectSpaceMock.Setup(Function(o) o.GetObject(It.IsAny(Of Object)())).Returns(Function(obj) obj) controller.SetupDemoTaskProperties(demoTasks, selectedChoiceActionItem, objectSpaceMock.Object, True) Assert.That(demoTasks.Select(Function(dt) dt.Priority), [Is].All.EqualTo(priority)) objectSpaceMock.Verify(Sub(o) o.CommitChanges()) 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 (current)
  4. How to unit test object queries by criteria and Detail View creation
  5. How to unit test event handlers in Controllers
  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.