XAF - Unit and Functional Testing with Blazor UI

XAF Team Blog
27 December 2019
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:

Showcase Your Apps on DevExpress.com

Highlight your business app and share your development experiences with the DevExpress community. To include your app in our upcoming App Showcase, please forward an application screenshot to clientservices@devexpress.com and tell us which DevExpress products you currently use within your organization.
8 comment(s)
harvinder singh 4
harvinder singh 4

- Why there are no validation on any form?

- Why position can not be drag to Group Panel?

- Can you change "Open" link to "Link" in case of linking

- Why Text search not working.?

- Under Link Tab, Click on New > Type Name - > Click Ok, It will not be linked.

28 December 2019
Anatol (DevExpress Support)
Anatol (DevExpress Support)
Thank you for your feedback, Harvinder. https://www.devexpress.com/go/XAF_Blazor_Demo.aspx is a pre-alpha version that has known issues. For more information about form validation and other features planned for 2020, see our rodmap.
30 December 2019
Wei  Chen
Wei Chen
Excuse me, when will the CTP version be released?
1 January 2020
Anatol (DevExpress Support)
Anatol (DevExpress Support)
Wei, it will be available in v20.1 - i.e., in the second quarter of 2020.  Please see our roadmap for additional information: eXpressApp Framework Roadmap 2020 - Your Vote Counts.
3 January 2020
Nikita Grigoryev
Nikita Grigoryev
Why Selenium if you have TestCafe? We use it and it's incredibly handy.
9 January 2020
Anatol (DevExpress Support)
Anatol (DevExpress Support)

We addressed this question in the 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 article:

Q: Why did you choose Selenium over TestCafe?
A: The primary reason is cross-platform EasyTest script language support. By using EasyTest scripts, we and our users are able to preserve most of our existing infrastructure and even tests from WinForms and WebForms ( = lower costs). As a bonus, Selenium helps us support XAF customers who write functional tests in C# or VB.NET (example
). While it is technically possible to create adapters for EasyTest->C# -> Node.js -> TestCafe, implementation and maintenance is much more difficult for ourselves and our customers.

10 January 2020
Manuel Grundner [DevExpress MVP]
Manuel Grundner [DevExpress MVP]

Hey Folks!

Any news on this? I am interested in the FunctionalTestBase but couldn't find any docs or sources around it.

22 August 2020
Dennis (DevExpress)
Dennis (DevExpress)
@Manuel: We have not yet published FunctionalTestBase and other testing APIs because they require polishing. Please track  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 for any updates in this regard.

3 September 2020

Please login or register to post comments.