Test Scenario
Let's imagine that we have a persistent class (Master) that has a non-associated reference property (Reference) and two associated collection properties for one-to-many (Children1) and many-to-many (Children2) relationships with different child classes.
C#public class Master : XPObject {
public Master(Session session) : base(session) { }
Reference reference;
string name;
public string Name {
get => name;
set => SetPropertyValue(nameof(Name), ref name, value);
}
public Reference Reference {
get => reference;
set => SetPropertyValue(nameof(Reference), ref reference, value);
}
[Association]
public XPCollection<Child1> Children1 {
get { return GetCollection<Child1>(nameof(Children1)); }
}
[Association]
public XPCollection<Child2> Children2 {
get { return GetCollection<Child2>(nameof(Children2)); }
}
}
public class Child1 : XPObject {
public Child1(Session session) : base(session) { }
Master master;
string name;
public string Name {
get => name;
set => SetPropertyValue(nameof(Name), ref name, value);
}
[Association]
public Master Master {
get => master;
set => SetPropertyValue(nameof(Master), ref master, value);
}
}
public class Child2 : XPObject {
public Child2(Session session) : base(session) { }
string name;
public string Name {
get => name;
set => SetPropertyValue(nameof(Name), ref name, value);
}
[Association]
public XPCollection<Master> Masters {
get {
return GetCollection<Master>(nameof(Masters));
}
}
}
public class Reference : XPObject {
public Reference(Session session) : base(session) { }
bool isDefault;
string name;
public string Name {
get => name;
set => SetPropertyValue(nameof(Name), ref name, value);
}
public bool IsDefault {
get => isDefault;
set => SetPropertyValue(nameof(IsDefault), ref isDefault, value);
}
}
Visual BasicPublic Class Master
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Private _reference As Reference
Private _name As String
Property Name As String
Get
Return _name
End Get
Set(ByVal Value As String)
SetPropertyValue(NameOf(Name), _name, Value)
End Set
End Property
Property Reference As Reference
Get
Return _reference
End Get
Set(ByVal Value As Reference)
SetPropertyValue(NameOf(Reference), _reference, Value)
End Set
End Property
<Association>
Public ReadOnly Property Children1() As XPCollection(Of Child1)
Get
Return GetCollection(Of Child1)(NameOf(Children1))
End Get
End Property
<Association>
Public ReadOnly Property Children2() As XPCollection(Of Child2)
Get
Return GetCollection(Of Child2)(NameOf(Children2))
End Get
End Property
End Class
Public Class Child1
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Private _master As Master
Private _name As String
Property Name As String
Get
Return _name
End Get
Set(ByVal Value As String)
SetPropertyValue(NameOf(Name), _name, Value)
End Set
End Property
<Association>
Property Master() As Master
Get
Return _master
End Get
Set(ByVal Value As Master)
SetPropertyValue(NameOf(Master), _master, Value)
End Set
End Property
End Class
Public Class Child2
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Private _name As String
Property Name As String
Get
Return _name
End Get
Set(ByVal Value As String)
SetPropertyValue(NameOf(Name), _name, Value)
End Set
End Property
<Association>
Public ReadOnly Property Masters() As XPCollection(Of Master)
Get
Return GetCollection(Of Master)(NameOf(Masters))
End Get
End Property
End Class
Public Class Reference
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Private _isDefault As Boolean
Private _name As String
Property Name As String
Get
Return _name
End Get
Set(ByVal Value As String)
SetPropertyValue(NameOf(Name), _name, Value)
End Set
End Property
Property IsDefault As Boolean
Get
Return _isDefault
End Get
Set(ByVal Value As Boolean)
SetPropertyValue(NameOf(IsDefault), _isDefault, Value)
End Set
End Property
End Class
We also implemented business logic for two scenarios that we want to unit test:
Scenario 1. Ensure that the Reference property is correctly initialized when a new Master object is created.
C#public class Master : XPObject {
public override void AfterConstruction() {
base.AfterConstruction();
Reference = Session.FindObject<Reference>(new BinaryOperator(nameof(Reference.IsDefault), true));
}
//...
}
Visual BasicPublic Class Master
Inherits XPObject
Public Overrides Sub AfterConstruction()
MyBase.AfterConstruction()
Reference = Session.FindObject(Of Reference)(New BinaryOperator(NameOf(Reference.IsDefault), True))
End Sub
'...
End Class
Scenario 2. Ensure that the associated collections of the Master object are correctly intialized
C#// This code can be in any place of your application.
Reference reference1 = new Reference(session);
reference1.IsDefault = true;
session.CommitChanges();
Master master = new Master(session);
Child1 child11 = new Child1(session);
child11.Name = "Child1";
child11.Master = master;
Child2 child21 = new Child2(session);
child21.Name = "Child2";
child21.Masters.Add(master);
Visual Basic'This code can be in any place of your application.
Dim reference1 As Reference = New Reference(session)
reference1.IsDefault = True
session.CommitChanges()
Dim master As Master = New Master(session)
Dim child11 As Child1 = New Child1(session)
child11.Name = "Child1"
child11.Master = master
Dim child21 As Child2 = New Child2(session)
child21.Name = "Child2"
child21.Masters.Add(master)
Unit Test Implementation
We want to test that the IsDefault property of the Reference object is true, that the Children1 collection contains the child11
object, and that the number of objects in the Children2 collection is 1.
C#Assert.AreEqual(master.Reference.IsDefault, true);
Assert.Contains(child11, master.Children1);
Assert.AreEqual(master.Children2.Count, 1);
Visual BasicAssert.AreEqual(master.Reference.IsDefault, True)
Assert.Contains(child11, master.Children1)
Assert.AreEqual(master.Children2.Count, 1)
Although it is possible to mock database access with Session and other XPO APIs as we demonstrated in other examples, this approach is cumbersome and error-prone when it comes to testing persistent objects.
For testing and demo purposes, XPO provides a special lightweight data store that keeps data in the memory - InMemoryDataStore. With InMemoryDataStore, you also won't need to create and drop a real database.
To make tests independent from data created by other tests and put logic to create a data store and a Session in a single place, it's recommended to create them in the SetUp method and dispose of them in the TearDown method. You may also consider avoiding the use of the default Session and set it to null in the OneTimeSetUp method.
C#IDataLayer dataLayer;
UnitOfWork session;
[OneTimeSetUp]
public void OneTimeSetUp() { XpoDefault.Session = null; }
[SetUp]
public void SetUp() {
dataLayer = new SimpleDataLayer(new InMemoryDataStore(AutoCreateOption.DatabaseAndSchema, false));
session = new UnitOfWork(dataLayer);
}
[TearDown]
public void TearDown() {
session.Dispose();
dataLayer.Dispose();
}
Visual BasicPrivate dataLayer As IDataLayer
Private session As UnitOfWork
<OneTimeSetUp>
Public Sub OneTimeSetUp()
XpoDefault.Session = Nothing
End Sub
<SetUp>
Public Sub SetUp()
dataLayer = New SimpleDataLayer(New InMemoryDataStore(AutoCreateOption.DatabaseAndSchema, False))
session = New UnitOfWork(dataLayer)
End Sub
<TearDown>
Public Sub TearDown()
session.Dispose()
dataLayer.Dispose()
End Sub
With this XPO setup, you can add test methods that access the 'session' variable and leave your business logic unchanged.
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
- 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 (current)