Test Scenario
We have the following Controller that copies properties of a current object to a new object. Our task is to test if the CreateLinkedSaleBase method correctly copies properties from one object to another. We don't need to test if the NewObjectViewController ObjectCreated event is raised. This is XAF's responsibility and this should be tested by functional tests.
C#public class CreateLinkedSaleBaseDescendantController : ViewController {
protected override void OnActivated() {
base.OnActivated();
NewObjectViewController standardController = Frame.GetController<NewObjectViewController>();
standardController.ObjectCreated += new EventHandler<ObjectCreatedEventArgs>(CreateLinkedSaleBaseDescendantController_ObjectCreated);
}
public void CreateLinkedSaleBaseDescendantController_ObjectCreated(object sender, ObjectCreatedEventArgs e) {
SaleBase createdObject = e.CreatedObject as SaleBase;
IObjectSpace objectSpace = e.ObjectSpace;
CreateLinkedSaleBase(objectSpace, createdObject);
}
public void CreateLinkedSaleBase(IObjectSpace objectSpace, SaleBase createdObject) {
NestedFrame nestedFrame = Frame as NestedFrame;
if (nestedFrame != null) {
SaleBase parentObject = objectSpace.GetObject(nestedFrame.ViewItem.CurrentObject as SaleBase);
if (createdObject != null) {
parentObject?.Copy(createdObject);
}
}
}
}
Visual BasicPublic 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)
Dim createdObject As SaleBase = TryCast(e.CreatedObject, SaleBase)
Dim objectSpace As IObjectSpace = e.ObjectSpace
CreateLinkedSaleBase(objectSpace, createdObject)
End Sub
Public Sub CreateLinkedSaleBase(ByVal objectSpace As IObjectSpace, ByVal createdObject As SaleBase)
Dim nestedFrame As NestedFrame = TryCast(Frame, NestedFrame)
If nestedFrame IsNot Nothing Then
Dim parentObject As SaleBase = objectSpace.GetObject(TryCast(nestedFrame.ViewItem.CurrentObject, SaleBase))
If createdObject IsNot Nothing Then
parentObject?.Copy(createdObject)
End If
End If
End Sub
End Class
The essential SaleBase class implementation part is:
C#public abstract class SaleBase : IObjectSpaceLink, IXafEntityObject, INotifyPropertyChanged {
[DevExpress.ExpressApp.DC.Aggregated]
[InverseProperty(nameof(SaleItem.SaleBase))]
public virtual IList<SaleItem> SaleItems { get; set; }
public decimal Discount {
get { return discount; }
set { discount = value; }
}
public void Copy(SaleBase target) {
target.Discount = Discount;
foreach(SaleItem saleItem in SaleItems) {
SaleItem saleItemNew = ObjectSpace.CreateObject<SaleItem>();
saleItemNew.Discount = saleItem.Discount;
saleItemNew.Product = saleItem.Product;
saleItemNew.Quantity = saleItem.Quantity;
saleItemNew.UpdateAmount();
target.SaleItems.Add(saleItemNew);
}
}
}
Visual BasicPublic MustInherit Class SaleBase
Implements IObjectSpaceLink, IXafEntityObject, INotifyPropertyChanged
<DevExpress.ExpressApp.DC.Aggregated>
<InverseProperty(NameOf(SaleItem.SaleBase))>
Public Overridable Property SaleItems As IList(Of SaleItem)
Public Property Discount As Decimal
Get
Return fDiscount
End Get
Set(ByVal value As Decimal)
fDiscount = value
End Set
End Property
Public Sub Copy(ByVal target As SaleBase)
target.Discount = Discount
For Each saleItem As SaleItem In SaleItems
Dim saleItemNew As SaleItem = ObjectSpace.CreateObject(Of SaleItem)()
saleItemNew.Discount = saleItem.Discount
saleItemNew.Product = saleItem.Product
saleItemNew.Quantity = saleItem.Quantity
saleItemNew.UpdateAmount()
target.SaleItems.Add(saleItemNew)
Next
End Sub
End Class
Unit Test Implementation
Let's create a test fixture class and a test method:
C#using System.Collections.Generic;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Editors;
using Moq;
using NUnit.Framework;
using MySolution.Module.Controllers;
using MySolution.Module.Data;
namespace MySolution.Tests {
[TestFixture]
public class CreateLinkedSaleBaseDescendantControllerTest {
[Test]
public void Test() {
}
}
}
Visual BasicImports DevExpress.ExpressApp
Imports DevExpress.ExpressApp.Editors
Imports Moq
Imports NUnit.Framework
Imports MySolution.Module
<TestFixture>
Public Class CreateLinkedSaleBaseDescendantControllerTest
<Test>
Public Sub Test()
End Sub
End Class
For our code, we need two SaleBase objects: one that will serve as a current (parent) object and another one as a newly created object. The SaleBase class is abstract. We will use mocks to create its instances. In addition, you need to mock the SaleItems property to return an empty SaleItems list to avoid a NullReference exception in the Copy method. Let's do this.
C#Mock<SaleBase> createdObjectMock = new Mock<SaleBase>();
Mock<SaleBase> parentObjectMock = new Mock<SaleBase>();
parentObjectMock.Object.Discount = 123;
parentObjectMock.SetupGet(m => m.SaleItems).Returns(new List<SaleItem>());
Visual BasicDim createdObjectMock As Mock(Of SaleBase) = New Mock(Of SaleBase)()
Dim parentObjectMock As Mock(Of SaleBase) = New Mock(Of SaleBase)()
parentObjectMock.Object.Discount = 123
parentObjectMock.SetupGet(Function(m) m.SaleItems).Returns(New List(Of SaleItem)())
Then, we need to mock an IObjectSpace object and its GetObject method:
C#Mock<IObjectSpace> objectSpaceMock = new Mock<IObjectSpace>();
objectSpaceMock.Setup(m => m.GetObject(It.Is<SaleBase>(s => s == parentObjectMock.Object))).Returns(parentObjectMock.Object);
Visual BasicDim objectSpaceMock As Mock(Of IObjectSpace) = New Mock(Of IObjectSpace)()
objectSpaceMock.Setup(Function(m) m.GetObject(It.[Is](Of SaleBase)(Function(s) s Is parentObjectMock.Object))).Returns(parentObjectMock.Object)
Our logic uses NestedFrame, its ViewItem and a current object in this ViewItem. ViewItem is an abstract class. We will use mocks to create its instance. The ViewItem constructor is:
C#public ViewItem(Type objectType, string id);
Our business logic does not use the ViewItem Id, we can pass any string to the constructor. The objectType parameter will be the SaleBase type. The NestedFrame class is implemented as follows:
C#public class NestedFrame : Frame {
public NestedFrame(XafApplication application, TemplateContext context, ViewItem viewItem, ICollection<Controller> controllers);
public ViewItem ViewItem { get; }
}
Our code does not use XafApplication and TemplateContext instances. We can pass nulls for them to create a NestedFrame instance.
C#Mock<ViewItem> viewItemMock = new Mock<ViewItem>(typeof(SaleBase), "testID");
viewItemMock.Object.CurrentObject = parentObjectMock.Object;
NestedFrame nestedFrame = new NestedFrame(null, null, viewItemMock.Object, new List<Controller>());
Visual BasicDim viewItemMock As Mock(Of ViewItem) = New Mock(Of ViewItem)(GetType(SaleBase), "testID")
viewItemMock.Object.CurrentObject = parentObjectMock.Object
Dim nestedFrame As NestedFrame = New NestedFrame(Nothing, Nothing, viewItemMock.Object, New List(Of Controller)())
Now, we can create our CreateLinkedSaleBaseDescendantController instance, register it in the created NestedFrame and its CreateLinkedSaleBase method:
C#CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController();
nestedFrame.RegisterController(controller);
controller.CreateLinkedSaleBase(objectSpaceMock.Object, createdObjectMock.Object);
Visual BasicDim controller As CreateLinkedSaleBaseDescendantController = New CreateLinkedSaleBaseDescendantController()
nestedFrame.RegisterController(controller)
controller.CreateLinkedSaleBase(objectSpaceMock.Object, createdObjectMock.Object)
Let's verify that the SaleBase Copy method was called and the Discount property of the created object is 123:
C#parentObjectMock.Verify();
Assert.That(123, Is.EqualTo(createdObjectMock.Object.Discount));
Visual BasicparentObjectMock.Verify()
Assert.That(123, [Is].EqualTo(createdObjectMock.Object.Discount))
That's it. The entire test method will be:
C#[Test]
public void Test() {
Mock<SaleBase> createdObjectMock = new Mock<SaleBase>();
Mock<SaleBase> parentObjectMock = new Mock<SaleBase>();
parentObjectMock.Object.Discount = 123;
parentObjectMock.SetupGet(m => m.SaleItems).Returns(new List<SaleItem>());
Mock<IObjectSpace> objectSpaceMock = new Mock<IObjectSpace>();
objectSpaceMock.Setup(m => m.GetObject(It.Is<SaleBase>(s => s == parentObjectMock.Object))).Returns(parentObjectMock.Object);
Mock<ViewItem> viewItemMock = new Mock<ViewItem>(typeof(SaleBase), "testID");
viewItemMock.Object.CurrentObject = parentObjectMock.Object;
NestedFrame nestedFrame = new NestedFrame(null, null, viewItemMock.Object, new List<Controller>());
CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController();
nestedFrame.RegisterController(controller);
controller.CreateLinkedSaleBase(objectSpaceMock.Object, createdObjectMock.Object);
parentObjectMock.Verify();
Assert.That(123, Is.EqualTo(createdObjectMock.Object.Discount));
}
Visual Basic<Test>
Public Sub Test()
Dim createdObjectMock As Mock(Of SaleBase) = New Mock(Of SaleBase)()
Dim parentObjectMock As Mock(Of SaleBase) = New Mock(Of SaleBase)()
parentObjectMock.Object.Discount = 123
parentObjectMock.SetupGet(Function(m) m.SaleItems).Returns(New List(Of SaleItem)())
Dim objectSpaceMock As Mock(Of IObjectSpace) = New Mock(Of IObjectSpace)()
objectSpaceMock.Setup(Function(m) m.GetObject(It.[Is](Of SaleBase)(Function(s) s Is parentObjectMock.Object))).Returns(parentObjectMock.Object)
Dim viewItemMock As Mock(Of ViewItem) = New Mock(Of ViewItem)(GetType(SaleBase), "testID")
viewItemMock.Object.CurrentObject = parentObjectMock.Object
Dim nestedFrame As NestedFrame = New NestedFrame(Nothing, Nothing, viewItemMock.Object, New List(Of Controller)())
Dim controller As CreateLinkedSaleBaseDescendantController = New CreateLinkedSaleBaseDescendantController()
nestedFrame.RegisterController(controller)
controller.CreateLinkedSaleBase(objectSpaceMock.Object, createdObjectMock.Object)
parentObjectMock.Verify()
Assert.That(123, [Is].EqualTo(createdObjectMock.Object.Discount))
End Sub
See Also:
This series contains the following articles:
- How to write unit tests for XAF Actions, Controllers, and other custom UI logic
- How to unit test Action's enabled/disabled state based on user permissions
- How to unit test whether object property changes via Actions were successfully committed
- How to unit test object queries by criteria and Detail View creation
- How to unit test event handlers in Controllers
- How to unit test New Action's custom business logic based on parent and nested Views (current)
- How to unit test Action's enabled/disabled state based on target criteria and selection dependency types in List View
- How to unit test Action's enabled/disabled state based on target criteria and selection dependency types in Detail View
- How to unit test localized strings from CaptionHelper (Approach 1)
- How to unit test localized strings from CaptionHelper (Approach 2)
- How to unit test custom logic in XAF/XPO business classes