Test Scenario
We have a Controller that enables/disables an Action depending on user permissions. Our task is to test the UpdateSetTaskActionState method to ensure that it correctly disables/enables the SetTask Action based on the user Write permissions for the Status and Priority properties of selected DemoTask objects. If a user can't write any of these properties, the Action is disabled. Otherwise, it's enabled.
C#public class TaskActionsController : ViewController {
private SingleChoiceAction setTaskAction;
public TaskActionsController()
setTaskAction = new SingleChoiceAction(this, "SetTaskAction", PredefinedCategory.Edit);
}
public SingleChoiceAction SetTaskAction { get => setTaskAction; }
protected override void OnActivated() {
base.OnActivated();
View.SelectionChanged += new EventHandler(View_SelectionChanged);
UpdateSetTaskActionState();
}
void View_SelectionChanged(object sender, EventArgs e) {
UpdateSetTaskActionState();
}
public void UpdateSetTaskActionState() {
bool isGranted = true;
foreach (object selectedObject in View.SelectedObjects) {
bool isPriorityGranted = SecuritySystem.IsGranted(new PermissionRequest(ObjectSpace, typeof(DemoTask), SecurityOperations.Write, selectedObject, nameof(DemoTask.Priority)));
bool isStatusGranted = SecuritySystem.IsGranted(new PermissionRequest(ObjectSpace, typeof(DemoTask), SecurityOperations.Write, selectedObject, nameof(DemoTask.Status)));
if (!isPriorityGranted || !isStatusGranted) {
isGranted = false;
}
}
SetTaskAction.Enabled.SetItemValue("SecurityAllowance", isGranted);
}
}
Visual BasicPublic Class TaskActionsController
Inherits ViewController
Private _setTaskAction As SingleChoiceAction
Protected Overrides Sub OnActivated()
MyBase.OnActivated()
AddHandler View.SelectionChanged, AddressOf View_SelectionChanged
UpdateSetTaskActionState()
End Sub
Private Sub View_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs)
UpdateSetTaskActionState()
End Sub
Public Sub UpdateSetTaskActionState()
Dim isGranted As Boolean = True
For Each selectedObject As Object In View.SelectedObjects
Dim isPriorityGranted As Boolean = SecuritySystem.IsGranted(New PermissionRequest(ObjectSpace, GetType(DemoTask), SecurityOperations.Write, selectedObject, "Priority"))
Dim isStatusGranted As Boolean = SecuritySystem.IsGranted(New PermissionRequest(ObjectSpace, GetType(DemoTask), SecurityOperations.Write, selectedObject, "Status"))
If Not isPriorityGranted OrElse Not isStatusGranted Then
isGranted = False
End If
Next selectedObject
SetTaskAction.Enabled.SetItemValue("SecurityAllowance", isGranted)
End Sub
Public Sub New()
_setTaskAction = New SingleChoiceAction(Me, "SetTaskAction", PredefinedCategory.Edit)
End Sub
Public ReadOnly Property SetTaskAction() As SingleChoiceAction
Get
Return _setTaskAction
End Get
End Property
End Class
Unit Test Implementation
Let's create a test fixture class to implement our test:
C#using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Security;
using MySolution.Module.Controllers;
using Moq;
using NUnit.Framework;
using System;
namespace MySolution.Tests {
[TestFixture]
public class SetTaskActionStateTest {
}
}
Visual BasicImports DevExpress.ExpressApp
Imports DevExpress.ExpressApp.Security
Imports MySolution.Module.Controllers
Imports Moq
Imports NUnit.Framework
<TestFixture()>
Public Class SetTaskActionStateTest
End Class
Our business logic depends on selected objects and the result the SecuritySystem.IsGranted method returns. The SetTask Action's Enabled state does not depend on what objects are selected. We can use any object types and mock the SecuritySystem.IsGranted method. In this article, we will use strings rather than DemoTask objects to illustrate the power of mocks. However, you can create real DemoTask objects and change the SecuritySystem.IsGranted method mock implementation to use these objects. You can find an example of how to use real DemoTask objects in the How to unit test that object property changes via Actions were successfully committed article.
First, we need a view with selected objects. Let's use the following method in our test class that will create it:
C#private View PrepareView(string[] selectedObjects) {
var mockView = new Mock<View>(/*isRoot:*/true);
mockView.Setup(v => v.SelectedObjects).Returns(selectedObjects);
return mockView.Object;
}
Visual BasicPrivate Function PrepareView(ByVal selectedObjects As String()) As View
Dim mockView = New Mock(Of View)(True)
mockView.Setup(Function(v) v.SelectedObjects).Returns(selectedObjects)
Return mockView.Object
End Function
In this method, we mock an XAF view and the passed array of strings will be used as selected objects in the view.
Then, we need to mock how the XAF Security System checks user permissions before each test and reset the XAF Security System after each test. Let's use the following methods to do this:
C#[SetUp]
public void Setup() {
var securityMock = new Mock<ISecurityStrategyBase>();
Func<PermissionRequest, bool> permissionFunc = r => (string)r.TargetObject == "Granted";
var requestSecurityMock = securityMock.As<IRequestSecurity>();
requestSecurityMock.Setup(s => s.IsGranted(CreatePermissionMatcher("Priority"))).Returns(permissionFunc);
requestSecurityMock.Setup(s => s.IsGranted(CreatePermissionMatcher("Status"))).Returns(permissionFunc);
SecuritySystem.SetInstance(securityMock.Object);
}
[TearDown]
public void TearDown() {
SecuritySystem.SetInstance(null);
}
PermissionRequest CreatePermissionMatcher(string name) {
return Match.Create<PermissionRequest>(s => s.MemberName == name);
}
Visual Basic<SetUp>
Public Sub Setup()
Dim securityMock = New Mock(Of ISecurityStrategyBase)()
Dim permissionFunc As Func(Of PermissionRequest, Boolean) = Function(r) CStr(r.TargetObject) = "Granted"
Dim requestSecurityMock = securityMock.[As](Of IRequestSecurity)()
requestSecurityMock.Setup(Function(s) s.IsGranted(CreatePermissionMatcher("Priority"))).Returns(permissionFunc)
requestSecurityMock.Setup(Function(s) s.IsGranted(CreatePermissionMatcher("Status"))).Returns(permissionFunc)
SecuritySystem.SetInstance(securityMock.Object)
End Sub
<TearDown>
Public Sub TearDown()
SecuritySystem.SetInstance(Nothing)
End Sub
Private Function CreatePermissionMatcher(ByVal name As String) As PermissionRequest
Return Match.Create(Of PermissionRequest)(Function(s) s.MemberName = name)
End Function
In the Setup method, we specify that a user can write the Priority and Status properties if a current object equals "Granted". Otherwise, a user can't.
Now, we can write our test. Its implementation will be the follows:
C#[Test]
[TestCase(new[] { "Granted", "Denied" }, false)]
[TestCase(new[] { "Granted", "Granted" }, true)]
public void TaskTest(string[] selectedObjects, bool allowed) {
TaskActionsController controller = new TaskActionsController();
controller.SetView(PrepareView(selectedObjects));
controller.UpdateSetTaskActionState();
Assert.That(allowed, Is.EqualTo(controller.SetTaskAction.Enabled["SecurityAllowance"]));
}
Visual Basic<Test>
<TestCase({"Granted", "Denied"}, False)>
<TestCase({"Granted", "Granted"}, True)>
Public Sub TaskTest(ByVal selectedObjects As String(), ByVal allowed As Boolean)
Dim controller As TaskActionsController = New TaskActionsController()
controller.SetView(PrepareView(selectedObjects))
controller.UpdateSetTaskActionState()
Assert.That(allowed, [Is].EqualTo(controller.SetTaskAction.Enabled("SecurityAllowance")))
End Sub
Here, we provide two test cases that are a list of selected objects and the expected enabled/disabled state of the SetTask Action, create our Controller and provide it with a view, then call the tested method and check the result.
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 (current)
- 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