KB Article T852589
Visible to All Users

How to unit test object queries by criteria and Detail View creation

Test Scenario
We have the following Controller that finds an object by a criterion and opens its Detail View on an action click. Our task is to check if we create the correct criterion, if the correct object is found, and if a Detail View is created for this object. We don't need to test that the Action Execute event is raised and that the Detail View is shown. This is XAF responsibility and this scenario should be tested by functional tests.

C#
public class FindBySubjectController : ViewController { private void FindBySubjectAction_Execute(object sender, ParametrizedActionExecuteEventArgs e) { var createdView = CreateObjectViewBySubject(((ListView)View).ObjectTypeInfo.Type, e.ParameterCurrentValue as string); e.ShowViewParameters.CreatedView = createdView; } public View CreateObjectViewBySubject(Type objectType, string subject) { CriteriaOperator criteria = CriteriaOperator.Parse($"Contains([Subject], '{subject}')"); IObjectSpace objectSpace = Application.CreateObjectSpace(objectType); object obj = objectSpace.FindObject(objectType, criteria); DetailView createdView = null; if (obj != null) { createdView = Application.CreateDetailView(objectSpace, obj); } return createdView; } }
Visual Basic
Public Class FindBySubjectController Inherits ViewController Private Sub FindBySubjectAction_Execute(ByVal sender As Object, ByVal e As ParametrizedActionExecuteEventArgs) Dim createdView As View = CreateObjectViewBySubject(CType(View, ListView).ObjectTypeInfo.Type, TryCast(e.ParameterCurrentValue, String)) e.ShowViewParameters.CreatedView = createdView End Sub Public Function CreateObjectViewBySubject(ByVal objectType As Type, ByVal subject As String) As View Dim criteria As CriteriaOperator = CriteriaOperator.Parse($"Contains([Subject], '{subject}')") Dim objectSpace As IObjectSpace = Application.CreateObjectSpace(objectType) Dim obj As Object = objectSpace.FindObject(objectType, criteria) Dim createdView As DetailView = Nothing If obj IsNot Nothing Then createdView = Application.CreateDetailView(objectSpace, obj) End If Return createdView End Function End Class

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

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

Let's create several objects and use one of them as the target object that we need to find:

C#
var objectType = typeof(DemoTask); var demoTasks = new List<DemoTask> { new DemoTask(Session.DefaultSession) {Subject = "Subject 1"}, new DemoTask(Session.DefaultSession) {Subject = "Subject 2"}, new DemoTask(Session.DefaultSession) {Subject = "Some subject 3"}, new DemoTask(Session.DefaultSession) {Subject = "Subject 4"}, }; const string targetSubject = "Some subject"; DemoTask targetDemoTask = demoTasks.First(dt => dt.Subject.Contains(targetSubject));
Visual Basic
Dim objectType = GetType(DemoTask) Dim demoTasks = New List(Of DemoTask) From { New DemoTask(Session.DefaultSession) With { .Subject = "Subject 1" }, New DemoTask(Session.DefaultSession) With { .Subject = "Subject 2" }, New DemoTask(Session.DefaultSession) With { .Subject = "Some subject 3" }, New DemoTask(Session.DefaultSession) With { .Subject = "Subject 4" } } Const targetSubject As String = "Some subject" Dim targetDemoTask As DemoTask = demoTasks.First(Function(dt) dt.Subject.Contains(targetSubject))

Our code uses IObjectSpace.FindObject to find an object and create a mock for it:

C#
var objectSpaceMock = new Mock<IObjectSpace>(); objectSpaceMock.Setup(o => o.FindObject( It.Is<Type>(t => t == objectType), It.Is<CriteriaOperator>(c => c.ToString() == $"Contains([Subject], '{targetSubject}')") )).Returns(targetDemoTask);
Visual Basic
Dim objectSpaceMock = New Mock(Of IObjectSpace)() objectSpaceMock.Setup(Function(o) o.FindObject(It.[Is](Of Type)(Function(t) t = objectType), It.[Is](Of CriteriaOperator)(Function(c) c.ToString() = $"Contains([Subject], '{targetSubject}')"))).Returns(targetDemoTask)

Then, we need an XafApplication instance. If we take a look at the XafApplication constructor (\DevExpress.ExpressApp\DevExpress.ExpressApp\XafApplication.cs), it calls the Initialize method.

C#
public XafApplication(ITypesInfo typesInfo) { Initialize(typesInfo); TryEnableOptimizations(); } public XafApplication() : this(XafTypesInfo.Instance) { }

The Initialize method checks if the passed typesInfo argument is not null.

C#
private void Initialize(ITypesInfo typesInfo) { Tick.In("new XafApplication()"); Guard.ArgumentNotNull(typesInfo, "typesInfo"); // ... }

Thus, to mock XafApplication, we also need to provide it with a mocked ITypesInfo object. Let's do this.

C#
var typeInfoMock = new Mock<ITypesInfo>(); var applicationMock = new Mock<XafApplication>(typeInfoMock.Object);
Visual Basic
Dim typeInfoMock = New Mock(Of ITypesInfo)() Dim applicationMock = New Mock(Of XafApplication)(typeInfoMock.Object)

Now, we need to mock the XafApplication.CreateObjectSpace method. This method is not virtual.

C#
public IObjectSpace CreateObjectSpace(Type objectType);

The Moq framework can't mock a not virtual class method. We can't mock the XafApplication.CreateObjectSpace method. To create our test, let's check how this method is implemented.

C#
public IObjectSpace CreateObjectSpace(Type objectType) { CheckCompatibility(); IObjectSpace objectSpace = CreateObjectSpaceCore(objectType); if(objectSpace == null) { objectSpace = CreateObjectSpaceCore(null); } if(!objectSpace.IsConnected) { objectSpace.Connected += new EventHandler(ObjectSpace_Connected); } OnObjectSpaceCreated(objectSpace); return objectSpace; }

This method calls the CreateObjectSpaceCore method to create an IObjectSpace instance. The CreateObjectSpaceCore method is virtual:

C#
protected virtual IObjectSpace CreateObjectSpaceCore(Type objectType);

We can mock the CreateObjectSpaceCore method instead of CreateObjectSpace.

In addition, the CreateObjectSpace method calls the CheckCompatibility method. We don't need its logic for our test. Let's prevent its logic from being executed. You could mock it, but this method is not virtual either.

C#
public void CheckCompatibility() { if(!IsCompatibilityChecked) { // ... } }

However, the CheckCompatibility method checks the IsCompatibilityChecked property and does nothing if it's true. This property is virtual and we can mock it.

C#
protected virtual bool IsCompatibilityChecked { get { return isCompatibilityChecked; } set { isCompatibilityChecked = value; } }

Finally, we can create our XafApplication mock:

C#
var typeInfoMock = new Mock<ITypesInfo>(); var applicationMock = new Mock<XafApplication>(typeInfoMock.Object); applicationMock.Protected().Setup<IObjectSpace>("CreateObjectSpaceCore", ItExpr.Is<Type>(t => t == objectType)).Returns(objectSpaceMock.Object); applicationMock.Protected().Setup<bool>("IsCompatibilityChecked").Returns(true);
Visual Basic
Dim typeInfoMock = New Mock(Of ITypesInfo)() Dim applicationMock = New Mock(Of XafApplication)(typeInfoMock.Object) applicationMock.Protected().Setup(Of IObjectSpace)("CreateObjectSpaceCore", ItExpr.[Is](Of Type)(Function(t) t = objectType)).Returns(objectSpaceMock.Object) applicationMock.Protected().Setup(Of Boolean)("IsCompatibilityChecked").Returns(True)

Now, let's mock the XafApplication CreateDetailView method. Only this overload is virtual:

C#
public virtual DetailView CreateDetailView(IObjectSpace objectSpace, string detailViewID, bool isRoot, object obj, bool isDelayedObjectLoading, IEnumerable objectsToPrefetch = null);

We need to mock this overload:

C#
DetailView expectedDetailView = new DetailView(objectSpaceMock.Object, targetDemoTask, applicationMock.Object, true); applicationMock.Setup(a => a.CreateDetailView( It.Is<IObjectSpace>(o => o == objectSpaceMock.Object), "", true, It.Is<object>(o => ReferenceEquals(o, targetDemoTask)), false, null)).Returns(expectedDetailView);
Visual Basic
Dim expectedDetailView As DetailView = New DetailView(objectSpaceMock.Object, targetDemoTask, applicationMock.Object, True) applicationMock.Setup(Function(a) a.CreateDetailView(It.[Is](Of IObjectSpace)(Function(o) o Is objectSpaceMock.Object), "", True, It.[Is](Of Object)(Function(o) ReferenceEquals(o, targetDemoTask)), False, Nothing)).Returns(expectedDetailView)

Now, we need to create our Controller, assign the created XafApplication instance to it, call the CreateObjectViewBySubject method and check that our methods were called and the correct Detail View is returned:

C#
FindBySubjectController controller = new FindBySubjectController(); controller.Application = applicationMock.Object; var currentDetailView = controller.CreateObjectViewBySubject(objectType, targetSubject); applicationMock.Verify(); Assert.That(currentDetailView, Is.EqualTo(expectedDetailView));
Visual Basic
Dim controller As FindBySubjectController = New FindBySubjectController() controller.Application = applicationMock.Object Dim currentDetailView = controller.CreateObjectViewBySubject(objectType, targetSubject) applicationMock.Verify() Assert.That(currentDetailView, [Is].EqualTo(expectedDetailView))

That appears to be all. Let's run our test. It fails with the following error:

Code
System.ArgumentException : An error with number 1021 has occurred. Error message: The object that has been passed belongs to another ObjectSpace. This error may occur when you manipulate your objects via an ObjectSpace which these objects do not belong to. For instance, you may receive this error when using the XafApplication.CreateDetailView method in case you passed an object that was obtained from a different ObjectSpace than the ObjectSpace used as a parameter in this method. To correct this error, you should ensure that all the objects you manipulate belong to one ObjectSpace. In most cases, to avoid this error it's sufficient to call the ObjectSpace.GetObject/GetObjectByKey methods to get a passed object in the target ObjectSpace. If this doesn't help, please contact our Support Team at http://www.devexpress.com/Support/Center/ DevExpress.ExpressApp.Utils.Guard.CheckObjectFromObjectSpace(IObjectSpace objectSpace, Object obj) DevExpress.ExpressApp.DetailView.set_CurrentObject(Object value) DevExpress.ExpressApp.DetailView..ctor(IModelDetailView info, IObjectSpace objectSpace, Object obj, XafApplication application, Boolean isRoot) DevExpress.ExpressApp.DetailView..ctor(IObjectSpace objectSpace, Object obj, XafApplication application, Boolean isRoot) MySolution.Tests.FindBySubjectActionExecuteTest.FindSubjectTest() in FindBySubjectActionExecuteTest.cs

We can also debug our test and follow the How to obtain the exception's call stack article to catch this exception. The exception is raised in the Guard.CheckObjectFromObjectSpace method. Let's check its implementation (\DevExpress.ExpressApp\DevExpress.ExpressApp\Utils\Guard.cs):

C#
public static void CheckObjectFromObjectSpace(IObjectSpace objectSpace, Object obj) { if((obj != null) && !objectSpace.Contains(obj)) { throw new ArgumentException(SystemExceptionLocalizer.GetExceptionMessage(ExceptionId.PassedObjectBelongsToAnotherObjectSpace)); } }

Our test fails because the IObjectSpace.Contains method returns null. You may face such issues that relay in XAF internals when creating your test. This approach will help you find these issues and overcome them. In our case, we need to mock the IObjectSpace.Contains method to avoid the issue.

C#
objectSpaceMock.Setup(o => o.Contains(It.Is<DemoTask>(dt => ReferenceEquals(dt, targetDemoTask)))).Returns(true);
Visual Basic
objectSpaceMock.Setup(Function(o) o.Contains(It.[Is](Of DemoTask)(Function(dt) ReferenceEquals(dt, targetDemoTask)))).Returns(True)

After this, our test passes. The entire test method code is the following:

C#
[Test] public void FindSubjectTest() { var objectType = typeof(DemoTask); var demoTasks = new List<DemoTask> { new DemoTask(Session.DefaultSession) {Subject = "Subject 1"}, new DemoTask(Session.DefaultSession) {Subject = "Subject 2"}, new DemoTask(Session.DefaultSession) {Subject = "Some subject 3"}, new DemoTask(Session.DefaultSession) {Subject = "Subject 4"}, }; const string targetSubject = "Some subject"; DemoTask targetDemoTask = demoTasks.First(dt => dt.Subject.Contains(targetSubject)); var objectSpaceMock = new Mock<IObjectSpace>(); objectSpaceMock.Setup(o => o.FindObject( It.Is<Type>(t => t == objectType), It.Is<CriteriaOperator>(c => c.ToString() == $"Contains([Subject], '{targetSubject}')") )).Returns(targetDemoTask); objectSpaceMock.Setup(o => o.Contains(It.Is<DemoTask>(dt => ReferenceEquals(dt, targetDemoTask)))).Returns(true); var typeInfoMock = new Mock<ITypesInfo>(); var applicationMock = new Mock<XafApplication>(typeInfoMock.Object); applicationMock.Protected().Setup<IObjectSpace>("CreateObjectSpaceCore", ItExpr.Is<Type>(t => t == objectType)).Returns(objectSpaceMock.Object); applicationMock.Protected().Setup<bool>("IsCompatibilityChecked").Returns(true); DetailView expectedDetailView = new DetailView(objectSpaceMock.Object, targetDemoTask, applicationMock.Object, true); applicationMock.Setup(a => a.CreateDetailView( It.Is<IObjectSpace>(o => o == objectSpaceMock.Object), "", true, It.Is<object>(o => ReferenceEquals(o, targetDemoTask)), false, null)).Returns(expectedDetailView); FindBySubjectController controller = new FindBySubjectController(); controller.Application = applicationMock.Object; var currentDetailView = controller.CreateObjectViewBySubject(objectType, targetSubject); applicationMock.Verify(); Assert.That(currentDetailView, Is.EqualTo(expectedDetailView)); }
Visual Basic
<Test> Public Sub FindSubjectTest() Dim objectType = GetType(DemoTask) Dim demoTasks = New List(Of DemoTask) From { New DemoTask(Session.DefaultSession) With { .Subject = "Subject 1" }, New DemoTask(Session.DefaultSession) With { .Subject = "Subject 2" }, New DemoTask(Session.DefaultSession) With { .Subject = "Some subject 3" }, New DemoTask(Session.DefaultSession) With { .Subject = "Subject 4" } } Const targetSubject As String = "Some subject" Dim targetDemoTask As DemoTask = demoTasks.First(Function(dt) dt.Subject.Contains(targetSubject)) Dim objectSpaceMock = New Mock(Of IObjectSpace)() objectSpaceMock.Setup(Function(o) o.FindObject(It.[Is](Of Type)(Function(t) t = objectType), It.[Is](Of CriteriaOperator)(Function(c) c.ToString() = $"Contains([Subject], '{targetSubject}')"))).Returns(targetDemoTask) objectSpaceMock.Setup(Function(o) o.Contains(It.[Is](Of DemoTask)(Function(dt) ReferenceEquals(dt, targetDemoTask)))).Returns(True) Dim typeInfoMock = New Mock(Of ITypesInfo)() Dim applicationMock = New Mock(Of XafApplication)(typeInfoMock.Object) applicationMock.Protected().Setup(Of IObjectSpace)("CreateObjectSpaceCore", ItExpr.[Is](Of Type)(Function(t) t = objectType)).Returns(objectSpaceMock.Object) applicationMock.Protected().Setup(Of Boolean)("IsCompatibilityChecked").Returns(True) Dim expectedDetailView As DetailView = New DetailView(objectSpaceMock.Object, targetDemoTask, applicationMock.Object, True) applicationMock.Setup(Function(a) a.CreateDetailView(It.[Is](Of IObjectSpace)(Function(o) o Is objectSpaceMock.Object), "", True, It.[Is](Of Object)(Function(o) ReferenceEquals(o, targetDemoTask)), False, Nothing)).Returns(expectedDetailView) Dim controller As FindBySubjectController = New FindBySubjectController() controller.Application = applicationMock.Object Dim currentDetailView = controller.CreateObjectViewBySubject(objectType, targetSubject) applicationMock.Verify() Assert.That(currentDetailView, [Is].EqualTo(expectedDetailView)) 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 (current)
  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.