OBSOLETE - Unit and Functional Testing with XAF's Blazor UI

XAF Team Blog
27 December 2019
EDIT: This article is now obsolete. Instead, refer to the Test Blazor UI Applications article.


I want to share our vision on unit/functional testing as early as possible, because it is important to XAF's target audience - development teams creating complex enterprise apps. Increased support traffic regarding this topic and our recent roadmap surveys underscore the importance of this subject. Everything I will describe below is already battle-tested by our team internally.

Functional Tests

We continue to use our cross-platform EasyTest engine for functional testing. Chrome or FireFox web browser interaction is powered by Selenium.

If you have Google Chrome installed on your desktop, you can run automatic tests for our online Blazor demo with Visual Studio or the command prompt - no special configuration is required. For more information, refer to the following KB article: How to use EasyTest and Selenium WebDriver to create functional tests for XAF's Blazor UI with C#, VB.NET or a human-readable scripting language.

The following are supported:

1. Functional tests with C# or VB.NET (for developers).

using DevExpress.ExpressApp.EasyTest.BlazorAdapter;
using DevExpress.ExpressApp.EasyTest.BlazorAdapter.TestControls;
using DevExpress.ExpressApp.EasyTest.BlazorAdapter.Utils;
using NUnit.Framework;

namespace MainDemo.Blazor.FunctionalTest {
    public class TakeScreenshortTests : FunctionalTestBase {
        public TakeScreenshortTests() {
            TestSettings.DefaultApplicationUrl = "https://demos.devexpress.com/XAF/BlazorDemo/";
        }
        [Test]
        public void StartPageScreenshot() {
            CompareScreenshot.Exec(TestSettings.CombineWithCWD(@"..\..\Images\MainDemo.png"), Browser.GetImage());
        }
        [Test]
        public void DeatailViewScreenshot() {
            var mainListView = new MainListView(Driver);
            mainListView.Grid.RowClick("Hewitt Aiello");
            CompareScreenshot.Exec(TestSettings.CombineWithCWD(@"..\..\Images\ContactMainDemo.png"), Browser.GetImage());
        }
        [Test]
        public void CRUD() {
            var contactsListView = new MainListView(Driver);
            contactsListView.Toolbar.FindControl("New").Act(null);
            var contactDetailView = new MainDetailView(Driver);
            ((IControlText)contactDetailView.Form.FindControl(TestControlType.Field, "First Name")).Text = "TestName";
            contactDetailView.Toolbar.FindControl("Save and Close").Act();
            contactsListView = new MainListView(Driver);
            contactsListView.Grid.RowClick("TestName");
            contactDetailView = new MainDetailView(Driver);
            contactDetailView.Toolbar.FindControl("Delete").Act();
            wait.Until(BlazorAppResponseAwaiter.IsAlertPresent);
            var dialog = new DialogTestControl(Driver);
            Assert.AreEqual("You are about to delete the selected record(s). Do you want to proceed?", dialog.Text);
            dialog.Act("Yes");
            wait.Until(SeleniumUtils.ElementExists(GridTestControl.ByGrid));
            contactsListView = new MainListView(Driver);
            Assert.AreEqual(0, contactsListView.Grid.WebElement.FindElements(GridTestControl.ByRowByValue("TestName")).Count);
        }
    }
}

2. Functional tests with human-readable scripts (for non-developers).

#Application MainDemoBlazor_Chrome

*Action New
*FillForm
 First Name = TestContact
*Action Save and Close

*ProcessRecord 
 Full Name = TestContact

*Action Delete

*HandleDialog
 Respond = Yes
 Message = You are about to delete the selected record(s). Do you want to proceed?

!ProcessRecord 
 Full Name = TestContact

Unit Tests

Thanks to XAF's modular architecture and mocks, we can keep our unit tests as lightweight as possible. Unit tests for Blazor UI are no different than XAF tests for WinForms or WebForms. Consider the following test cases: 

1. Test whether a Controller's Action is active in DetailView and ListView.

#if DebugTest
using DevExpress.ExpressApp.Base.Tests;
using DevExpress.ExpressApp.Blazor.SystemModule;
using DevExpress.ExpressApp.Tests.Blazor.Helpers;
using NUnit.Framework;

namespace DevExpress.ExpressApp.Tests.Blazor.SystemModule {
    [TestFixture]
    public class ModificationsControllerTests : BaseDBTest {
        [Test]
        public void TestUpdateActionState() {
            DetailView detailView = new TestDetailViewFactory(this).CreateView<TestPersistentObject>(true);
            BlazorModificationsController modificationsController = new BlazorModificationsController();
            modificationsController.SetView(detailView);
            Assert.IsTrue(modificationsController.SaveAction.Active["OnlyForDetailView"]);
            Assert.IsTrue(modificationsController.SaveAndCloseAction.Active["OnlyForDetailView"]);
            Assert.IsTrue(modificationsController.SaveAndNewAction.Active["OnlyForDetailView"]);
            Assert.IsTrue(modificationsController.CancelAction.Active["OnlyForDetailView"]);

            ListView listView = new TestListViewFactory(this).CreateView<TestPersistentObject>();
            modificationsController = new BlazorModificationsController();
            modificationsController.SetView(listView);
            Assert.IsFalse(modificationsController.SaveAction.Active["OnlyForDetailView"]);
            Assert.IsFalse(modificationsController.SaveAndCloseAction.Active["OnlyForDetailView"]);
            Assert.IsFalse(modificationsController.SaveAndNewAction.Active["OnlyForDetailView"]);
            Assert.IsFalse(modificationsController.CancelAction.Active["OnlyForDetailView"]);
        }
//...
}

2. Test whether a boolean property editor has localized values in the underlying control based on the Application Model.

//...
[Test]
public void SelectedItemTest() {
    ModelPropertyEditorImpl model = new ModelPropertyEditorImpl("Bool");
    model.CaptionForFalse = "CaptionForFalse";
    model.CaptionForTrue = "CaptionForTrue";
    BooleanPropertyEditor propertyEditor = new BooleanPropertyEditor(typeof(TestObjectWithNullableProperties), model);

    propertyEditor.ComponentValue = null;
    Assert.IsNull(propertyEditor.SelectedItem);
    Assert.IsNull(propertyEditor.ComponentValue);

    propertyEditor.ComponentValue = true;
    Assert.AreEqual(model.CaptionForTrue, propertyEditor.SelectedItem.Caption);
    Assert.AreEqual(propertyEditor.ComponentValue, propertyEditor.SelectedItem.Value);

    propertyEditor.ComponentValue = false;
    Assert.AreEqual(model.CaptionForFalse, propertyEditor.SelectedItem.Caption);
    Assert.AreEqual(propertyEditor.ComponentValue, propertyEditor.SelectedItem.Value);
}
//...

Future Plans

Ultimately, we will provide more documentation and release all required assemblies with helper/mock classes (like TestDBTest, TestDetailViewFactory, BlazorAdapter, etc.) - all to reduce the time needed to write unit and functional tests with XAF. Additional enhancements are expected and will be driven by user feedback. If you have unresolved testing issues or usage scenarios, please post your feedback below.

Ready to start testing today? Please review the following articles to learn more:

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.