Skip to main content
All docs
V26.1
  • EF Core Unit Tests

    • 12 minutes to read

    You can use Moq and xUnit to write lightweight unit tests for XAF Controllers, Actions, and other custom UI logic. Unlike EasyTest, this approach does not require a running XAF application instance and allows you to test isolated components.

    This topic lists ways to test different parts of an XAF application.

    1. Test Action state based on user permissions
    2. Test whether an Action changes property values
    3. Test event handlers in Controllers
    4. Test Action state based on target criteria in Detail View
    5. Test Action state based on selection dependency in List View
    6. Test New Action Custom Business Logic (New Object Inherits Parent Property Values)
    7. Test object queries by criteria and Detail View creation
    8. Test localized strings from CaptionHelper
    9. Web API Service Integration Tests

    Tip

    You can find the test examples described in this section in the following GitHub repositories:

    Test Action State Based on User Permissions

    Test Scenario

    The TaskActionsControllerenables/disables the SetTaskAction depending on user permissions.

    A test must ensure that the UpdateSetTaskActionState method correctly enables or disables SetTaskAction based on the user’s Write permissions for the Status and Priority properties of selected DemoTask objects. If the user cannot write either of these properties, the action is disabled.

    Display TaskActionsController.cs
    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);
    }
    

    Unit Test Implementation

    The following code snippet verifies that the SetTaskAction is enabled or disabled based on security permissions for selected objects:

    [Theory]
    [InlineData(new[] { "Granted", "Denied" }, false)]
    [InlineData(new[] { "Granted", "Granted" }, true)]
    public void TaskTest(string[] selectedObjects, bool allowed) {
        using TaskActionsController controller = new TaskActionsController();
    
        controller.SetView(PrepareView(selectedObjects));
        controller.UpdateSetTaskActionState();
        Assert.Equal(allowed, controller.SetTaskAction.Enabled["SecurityAllowance"]);
    }
    

    GitHub Files to review:

    Test Whether an Action Changes Property Values

    Test Scenario

    The TaskActionsController implements the SetTaskAction that allows users to change the Priority and Status property values of the selected object.

    A test must check that the Action changes the Priority property and commits the result.

    Display TaskActionsController.cs
    public SingleChoiceAction SetTaskAction;
    
    public TaskActionsController() {
        TargetObjectType = typeof(DemoTask);
    
        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 SetTaskAction_Execute(object sender, SingleChoiceActionExecuteEventArgs args) {
        bool viewIsListview = View is ListView;
    
        IObjectSpace objectSpace = viewIsListview
            ? Application.CreateObjectSpace(typeof(DemoTask))
            : View.ObjectSpace;
    
    
        SetupDemoTaskProperties(args.SelectedObjects, args.SelectedChoiceActionItem, objectSpace, viewIsListview);
    
        if(viewIsListview) {
            View.ObjectSpace.Refresh();
        }
    }
    // ...
    public void SetupDemoTaskProperties(IList selectedObjects, ChoiceActionItem selectedChoiceActionItem, IObjectSpace objectSpace, bool shouldCommitChanges) {
        foreach(object obj in selectedObjects) {
            if(objectSpace.GetObject(obj) is DemoTask objInNewObjectSpace) {
                if(selectedChoiceActionItem.ParentItem == setPriorityItem) {
                    objInNewObjectSpace.Priority = (Priority)selectedChoiceActionItem.Data;
                } else if(selectedChoiceActionItem.ParentItem == setStatusItem) {
                    objInNewObjectSpace.Status = (TaskStatus)selectedChoiceActionItem.Data;
                }
            }
        }
        if(shouldCommitChanges) {
            objectSpace.CommitChanges();
        }
    }
    

    Unit Test Implementation

    The following code snippet tests whether SetTaskAction correctly sets the Priority property for selected DemoTask objects and commits the changes:

    [Fact]
    public void SetTaskActionSetPriorityTest() {
        DemoTask[] demoTasks = {
            new DemoTask(){Priority=Priority.Low},
            new DemoTask(){Priority=Priority.Normal}
        };
    
        using 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.All(demoTasks, dt => Assert.Equal(priority, dt.Priority));
        objectSpaceMock.Verify(o => o.CommitChanges(), Times.Once);
    }
    

    GitHub Files to review:

    Test Event Handlers in Controllers

    Test Scenario

    The CreateLinkedSaleBaseDescendantController subscribes to the NewObjectViewController.ObjectCreated event.

    A test must check that NewObjectViewController exists and that the correct handler is subscribed to the ObjectCreated event.

    Display CreateLinkedSaleBaseDescendantController.cs
    public class CreateLinkedSaleBaseDescendantController :ViewController {
        protected override void OnActivated() {
            base.OnActivated();
            newObjectController = Frame.GetController<NewObjectViewController>();
            newObjectController.ObjectCreated += CreateLinkedSaleBaseDescendantController_ObjectCreated;
        }
        NewObjectViewController newObjectController;
        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);
                }
            }
        }
        // ...
    }
    

    Unit Test Implementation

    The following code snippet verifies that the CreateLinkedSaleBaseDescendantController correctly subscribes its CreateLinkedSaleBaseDescendantController_ObjectCreated handler to the NewObjectViewController.ObjectCreated event.

    [Fact]
    public void ControllerEventSubscriptionTest() {
        using NewObjectViewController newObjectViewController = new NewObjectViewController();
        using CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController();
        using Frame frame = new Frame(null, null);
    
        frame.RegisterController(newObjectViewController);
        frame.RegisterController(controller);
    
        controller.Active.Clear();
        controller.Active["Test"] = true;
    
        string targetMethodName = nameof(CreateLinkedSaleBaseDescendantController.CreateLinkedSaleBaseDescendantController_ObjectCreated);
        VerifyEventSubscription(
            newObjectViewController,
            nameof(newObjectViewController.ObjectCreated),
            controller,
            $"Void {targetMethodName}({typeof(object)}, {typeof(ObjectCreatedEventArgs)})"
        );
    }
    
    static void VerifyEventSubscription(object objectWithEvent, string eventName, object subscriber, string methodSignature) {
        var allBindings = BindingFlags.IgnoreCase | BindingFlags.Public |
                          BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
    
        var type = objectWithEvent.GetType();
        var fieldInfo = type.GetField(eventName, allBindings);
    
        Assert.NotNull(fieldInfo);
    
        var eventHandler = fieldInfo.GetValue(objectWithEvent) as Delegate;
    
        Assert.NotNull(eventHandler);
    
        Assert.Equal(subscriber, eventHandler.Target);
        Assert.Equal(methodSignature, eventHandler.Method.ToString());
    }
    

    GitHub Files to review:

    Test Action State Based on Target Criteria in Detail View

    Test Scenario

    A Controller enables a Detail View Action if the current object matches the Action’s target criteria.

    A test must ensure that the Detail View Action is enabled only when the current object matches the action’s target criteria.

    Unit Test Implementation

    The following code snippet verifies that XAF correctly evaluates a SimpleAction’s TargetObjectsCriteria in a Detail View. It checks that the action configured for objects with Priority = 2 is enabled for a high-priority DemoTask and disabled for a low-priority DemoTask.

    [Theory]
    [InlineData(Priority.High, true)]
    [InlineData(Priority.Low, false)]
    public void ActionsInDetailViewTest(Priority priority, bool result) {
        using ActionsCriteriaViewController actionsCriteriaViewController = new ActionsCriteriaViewController();
        using ViewController testedController = new ViewController();
    
        SimpleAction testedAction = new SimpleAction(testedController, "High Priority", string.Empty);
        testedAction.TargetObjectsCriteria = "Priority = 2";
    
        DemoTask task = new DemoTask();
        task.Priority = priority;
    
        var objectSpaceMock = new Mock<IObjectSpace>();
        objectSpaceMock.Setup(os => os.Contains(It.IsAny<object>())).Returns(true);
        objectSpaceMock.Setup(os => os.GetEvaluatorContextDescriptor(typeof(DemoTask))).Returns(new EvaluatorContextDescriptorDefault(typeof(DemoTask)));
        objectSpaceMock.Setup(os => os.GetObject(It.IsAny<object>())).Returns<object>(obj => obj);
    
        ITypesInfo info = new TypesInfo();
        info.LoadTypes(Assembly.GetAssembly(typeof(DemoTask)));
        objectSpaceMock.Setup(os => os.TypesInfo).Returns(info);
    
        var applicationMock = new Mock<XafApplication>();
        using DetailView detailView = new DetailView(objectSpaceMock.Object, task, applicationMock.Object, true);
        using Frame frame = new Frame(applicationMock.Object, TemplateContext.View, actionsCriteriaViewController, testedController);
    
        frame.SetView(detailView);
        Assert.Equal(result, testedAction.Enabled.ResultValue);
    }
    

    GitHub File to review:

    Test Action State Based on Selection Dependency in List View

    Test Scenario

    A Controller enables an Action if records selected in the List View meet particular criteria.

    A test must ensure that the List View Action is enabled or disabled correctly based on selected objects, according to the Action’s selection dependency and target criteria evaluation mode.

    Unit Test Implementation

    The following code snippet tests whether an XAF SimpleAction is enabled in a List View depending on:

    • The Action’s SelectionDependencyType
    • The Action’s TargetObjectsCriteriaMode
    • The current selected objects
    [Theory]
    [InlineData(SelectionDependencyType.Independent, TargetObjectsCriteriaMode.TrueAtLeastForOne, true)]
    [InlineData(SelectionDependencyType.Independent, TargetObjectsCriteriaMode.TrueForAll, false)]
    [InlineData(SelectionDependencyType.RequireSingleObject, TargetObjectsCriteriaMode.TrueAtLeastForOne, false)]
    [InlineData(SelectionDependencyType.RequireMultipleObjects, TargetObjectsCriteriaMode.TrueAtLeastForOne, true)]
    [InlineData(SelectionDependencyType.RequireMultipleObjects, TargetObjectsCriteriaMode.TrueForAll, false)]
    public void ActionsInListViewTest(SelectionDependencyType dependencyType, TargetObjectsCriteriaMode criteriaMode, bool result) {
        using ViewController testedController = new ViewController();
    
        SimpleAction testedAction = new SimpleAction(testedController, "High Priority", string.Empty);
        testedAction.TargetObjectsCriteria = "Priority = 2";
        testedAction.SelectionDependencyType = dependencyType;
        testedAction.TargetObjectsCriteriaMode = criteriaMode;
    
        List<DemoTask> tasks = new List<DemoTask> {
            new DemoTask() {Priority = Priority.High},
            new DemoTask() {Priority = Priority.Low}
        };
    
        using ActionsCriteriaViewController actionsCriteriaViewController = new ActionsCriteriaViewController();
    
        var applicationMock = new Mock<XafApplication>();
    
        var listEditorMock = new Mock<ListEditor>();
        listEditorMock.Setup(e => e.GetSelectedObjects()).Returns(tasks);
        listEditorMock.Setup(e => e.SupportsDataAccessMode(It.IsAny<CollectionSourceDataAccessMode>())).Returns(true);
        listEditorMock.Setup(e => e.SelectionType).Returns(SelectionType.Full);
    
        var objectSpaceMock = new Mock<IObjectSpace>();
        objectSpaceMock.Setup(os => os.GetEvaluatorContextDescriptor(typeof(DemoTask))).Returns(new EvaluatorContextDescriptorDefault(typeof(DemoTask)));
    
        ITypesInfo info = new TypesInfo();
        info.LoadTypes(Assembly.GetAssembly(typeof(DemoTask)));
        objectSpaceMock.Setup(os => os.TypesInfo).Returns(info);
    
        using ListView listView = new ListView(new CollectionSource(objectSpaceMock.Object, typeof(DemoTask)), listEditorMock.Object);
        using Frame frame = new Frame(applicationMock.Object, TemplateContext.View, actionsCriteriaViewController, testedController);
    
        frame.SetView(listView);
        Assert.Equal(result, testedAction.Enabled.ResultValue);
    }
    

    GitHub File to review:

    Test New Action Custom Business Logic (New Object Inherits Parent Property Values)

    Test Scenario

    The CreateLinkedSaleBaseDescendantController copies properties of a current object to a new object.

    A test must ensure that the CreateLinkedSaleBase method correctly copies the parent object’s properties to the newly created linked object.

    Display CreateLinkedSaleBaseDescendantController.cs
    public class CreateLinkedSaleBaseDescendantController :ViewController {
        protected override void OnActivated() {
            base.OnActivated();
            newObjectController = Frame.GetController<NewObjectViewController>();
            newObjectController.ObjectCreated += CreateLinkedSaleBaseDescendantController_ObjectCreated;
        }
        NewObjectViewController newObjectController;
        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);
                }
            }
        }
        // ...
    }
    

    Unit Test Implementation

    The following code snippet tests that the CreateLinkedSaleBase method copies relevant property values from the current parent SaleBase object to the newly created linked SaleBase object.

    [Fact]
    public void TestObjectPropertiesCopy() {
        Mock<IObjectSpace> objectSpaceMock = new Mock<IObjectSpace>();
        objectSpaceMock.Setup(m => m.CreateObject<SaleBase>()).Returns(() => {
            var objMock = new Mock<SaleBase>();
            objMock.SetupAllProperties();
            objMock.Object.SaleItems = new List<SaleItem>();
            return objMock.Object;
        });
        var createdObject = objectSpaceMock.Object.CreateObject<SaleBase>();
        var parentObject = objectSpaceMock.Object.CreateObject<SaleBase>();
        parentObject.Discount = 123m;
        objectSpaceMock.Setup(m => m.GetObject(It.Is<SaleBase>(s => s == parentObject))).Returns(parentObject);
    
        ITypesInfo info = new TypesInfo();
        info.LoadTypes(Assembly.GetAssembly(typeof(SaleItem)));
        objectSpaceMock.Setup(os => os.TypesInfo).Returns(info);
    
        Mock<ViewItem> viewItemMock = new Mock<ViewItem>(typeof(SaleBase), "testID");
        viewItemMock.Object.CurrentObject = parentObject;
    
        using NestedFrame nestedFrame = new NestedFrame(null, null, viewItemMock.Object, new List<Controller>());
        using CreateLinkedSaleBaseDescendantController controller = new CreateLinkedSaleBaseDescendantController();
    
        nestedFrame.RegisterController(controller);
        controller.CreateLinkedSaleBase(objectSpaceMock.Object, createdObject);
        Assert.Equal(123, createdObject.Discount);
    }
    

    GitHub Files to review:

    Test Object Queries by Criteria and Detail View Creation

    Test Scenario

    The FindBySubjectController finds an object by a criterion and opens its Detail View on an Action click.

    A test must check that the controller can find a DemoTask by a subject substring and create a Detail View for the found object.

    Display FindBySubjectController.cs
    public class FindBySubjectController :ViewController {
        public FindBySubjectController() {
            TargetObjectType = typeof(DemoTask);
            ParametrizedAction findBySubjectAction =
                new ParametrizedAction(this, "FindBySubjectAction", PredefinedCategory.View, typeof(string)) {
                    ImageName = "Action_Search",
                    NullValuePrompt = "Find task by subject…"
                };
            findBySubjectAction.Execute += FindBySubjectAction_Execute;
        }
    
        private void FindBySubjectAction_Execute(object sender, ParametrizedActionExecuteEventArgs e) {
            string paramValue = e.ParameterCurrentValue as string;
            var createdView = CreateObjectViewBySubject(View.ObjectTypeInfo.Type, paramValue as string);
            e.ShowViewParameters.CreatedView = createdView;
        }
    
        public View CreateObjectViewBySubject(Type objectType, string subject) {
            CriteriaOperator criteria = CriteriaOperator.FromLambda<DemoTask>(t => t.Subject.Contains(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;
        }
    }
    

    Unit Test Implementation

    The following code snippet tests that the Controller finds a DemoTask by subject text and opens it in a Detail View.

    [Fact]
    public void FindSubjectTest() {
        var objectType = typeof(DemoTask);
    
        const string targetSubject = "Some subject";
        DemoTask targetDemoTask = new DemoTask() { Subject = "Some subject 3" };
        var objectSpaceMock = new Mock<IObjectSpace>();
        objectSpaceMock.Setup(o => o.FindObject(
                It.Is<Type>(t => t == objectType),
                It.Is<CriteriaOperator>(c => c.ToString() == CriteriaOperator.FromLambda<DemoTask>(t => t.Subject.Contains(targetSubject)).ToString())
            ))
            .Returns(targetDemoTask);
    
        objectSpaceMock.Setup(o => o.Contains(
                It.Is<DemoTask>(dt => ReferenceEquals(dt, targetDemoTask))
            ))
            .Returns(true);
    
        ITypesInfo info = new TypesInfo();
        info.LoadTypes(Assembly.GetAssembly(typeof(DemoTask)));
        objectSpaceMock.Setup(os => os.TypesInfo).Returns(info);
    
        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);
    
        using 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);
    
        using FindBySubjectController controller = new FindBySubjectController();
    
        controller.Application = applicationMock.Object;
    
        var currentDetailView = controller.CreateObjectViewBySubject(objectType, targetSubject);
    
        applicationMock.Verify();
    
        Assert.Equal(expectedDetailView, currentDetailView);
    }
    

    GitHub Files to review:

    Test Localized Strings from CaptionHelper

    Test Scenario

    The Application Model contains localized strings. A test must ensure that the localized strings are correctly applied.

    <Localization>
      <LocalizationGroup Name="Messages">
        <LocalizationItem Name="TestString" Value="Test" IsNewNode="True" />
      </LocalizationGroup>
    </Localization>
    

    Unit Test Implementation. Approach 1

    The following code snippet verifies that the CaptionHelper.GetLocalizedText method correctly retrieves a localized string ("Test") from the Application Model by manually constructing the model layer.

    [Fact]
    public void GetLocalizedTextTest_ModelApplicationWay() {
        XAFUnitTestEFCoreModule module = new XAFUnitTestEFCoreModule();
    
        ITypesInfo info = new TypesInfo();
        info.LoadTypes(Assembly.GetAssembly(typeof(XAFUnitTestEFCoreEFCoreDbContext)));
    
        ModelApplicationCreatorProperties properties = ModelApplicationCreatorProperties.CreateDefault(info);
        ModelApplicationCreator modelApplicationCreator = ModelApplicationCreator.GetModelApplicationCreator(properties);
        ModelApplicationBase modelApplicationBase = modelApplicationCreator.CreateModelApplication();
        module.DiffsStore.Load(modelApplicationBase);
        IModelApplication modelApplication = (IModelApplication)modelApplicationBase;
        CaptionHelper.Setup(modelApplication);
        Assert.Equal("Test", CaptionHelper.GetLocalizedText("Messages", "TestString"));
    }
    

    Unit Test Implementation. Approach 2

    The following code snippet verifies that the CaptionHelper.GetLocalizedText method correctly retrieves a localized string ("Test") from the Application Model after the application is initialized via ExpressApplicationSetupParameters.

    [Fact]
    public void GetLocalizedTextTest_ExpressApplicationSetupParametersWay() {
        var applicationMock = new Mock<XafApplication>();
        applicationMock.CallBase = true;
    
        ModuleList moduleList = new ModuleList {
            new XAFUnitTestEFCoreModule()
        };
    
        var entityStoreMock = new Mock<IEntityStore>();
        var objectSpaceProviderMock = new Mock<IObjectSpaceProvider>();
        objectSpaceProviderMock.Setup(o => o.EntityStore).Returns(entityStoreMock.Object);
    
        ExpressApplicationSetupParameters setupParameters = new ExpressApplicationSetupParameters(null, objectSpaceProviderMock.Object, new ControllersManager(), moduleList);
        applicationMock.Object.Setup(setupParameters);
    
        Assert.Equal("Test", CaptionHelper.GetLocalizedText("Messages", "TestString"));
    }
    

    GitHub Files to review:

    Web API Service Integration Tests

    For end-to-end scenarios that test CRUD operations, authentication, and business object Actions through a hosted Web API service, use the Microsoft.AspNetCore.TestHost infrastructure. You can find a number of test examples in our MainDemo.NET.EFCore demo application, in the MainDemo.E2E.Tests project. Some of the tests are listed below:

    Check an Unauthorized Access
    The test verifies that unauthenticated requests return a 401 Unauthorized status code.
    Get Business Objects
    The test authenticates and retrieves business objects from the Web API.
    Create and Delete a Business Object
    The test creates an ApplicationUser, verifies the creation, then deletes it.
    Get a Business Object with a Reference Property
    The test retrieves an ApplicationUser with its UserLogins reference expanded.
    Assign an Object to a Reference Property
    The test creates an Employee, assigns a Department to it via the $ref endpoint, and verifies the link.
    Add an Object to a Collection
    The test creates a DemoTask, adds it to an Employee’s Tasks collection via the $ref endpoint, and verifies the association.
    Deep Create (Nested Objects in POST)
    The test creates an Employee with nested Tasks in a single POST request.
    Deep Update (Create a Reference Object via PATCH)
    The test creates an Employee, then uses a PATCH request to create a nested Department with positions.
    Invoke a Business Object Action
    The test creates a DemoTask, invokes the Postpone Action endpoint, and verifies that the DueDate has been postponed.
    Test Validation
    The test verifies that the Web API returns a validation error when required properties are missing.
    Test Localization Endpoints
    The test retrieves localized captions from the Localization endpoint with different Accept-Language headers.