How to write EasyTests in code

XAF Team Blog
04 May 2011

Related blogs:
Thinking of Testing… Enter EasyTest
How to implement a custom EasyTest command
EasyTest and test data

EasyTest language contains less syntactic sugar, so why should I care writing my scripts in C# or VB.Net?

I will answer using the words of our good customer Martin Brekhof: The main idea is that when I want to run the same test with (a lot off) different field values using for example a database that this would be easier to accomplish using C#. Since last week i could also see the benefit of something like a report tool for these tests (putting the testresults in a database and use a XAF app to create reports etc.).

Another customer (good too!!) Robert Anderson shared his implementation of an EasyTest script parser and pointed: running EasyTests from code allows our testers to create their own tests which can be integrated into the build process with zero efforts

[Test]
public void UserTest_ChangePasswordOnFirstLogon()
{
    RunTest(@"UserTests\ChangePasswordOnFirstLogon.ets");
}

The full discussion is available on this forum thread

However EasyTest architecture is flexible enough to allow writing tests from code without having to parse or create (*.ets) files.

Our team has prepared an example that demonstrates how to create EasyTest scripts in C#. The sample application is based on the Contact and DemoTask business classes taken from MainDemo. An in-memory data store is used for testing, which is performed via NUnit.

Writing tests

The test methods are located in the CommonTests, WebTests and WinTests classes. These classes are derived from a custom base class, providing auxiliary functionality, such as starting and stopping the application for testing. If you look at the test methods, you will see that they look like regular unit tests with lots of ‘Asserts’.  Basically, these tests consist of three different types of actions.
Firstly, you can invoke the adapter.CreateControl method to access application controls. After you get a control, you can query its status via the control.GetInterface method. You will need to specify a valid interface as the method parameter. Most of these interfaces reside in the DevExpress.EasyTest.Framework namespace and begin with ‘IControl’.


image

After you have cast the retrieved control to an interface this way, you can check specific properties, such as Enabled, or invoke methods, such as GetRowCount.
Secondly, you can use commandAdapter to execute basic commands, such as SetFieldValue. The commandAdapter is an instance of the TestCommandAdapter class, which wraps basic EasyTest commands. This class is implemented in the TestCommandAdapter file. Thirdly, you can use the NUnit.Framework.Assert method to check whether a test is passed correctly. Here is a sample test :

        public void ChangeContactNameTest_() {

            ITestControl control = adapter.CreateTestControl(TestControlType.Table, "");

            IGridBase table = control.GetInterface<IGridBase>();

            Assert.AreEqual(2, table.GetRowCount());

 

            List<IGridColumn> columns = new List<IGridColumn>(table.Columns);

            IGridColumn column = commandAdapter.GetColumn(control, "Full Name");

 

            Assert.AreEqual("John Nilsen", table.GetCellValue(0, column));

            Assert.AreEqual("Mary Tellitson", table.GetCellValue(1, column));

 

            commandAdapter.ProcessRecord("Contact", new string[] { "Full Name" }, new string[] { "Mary Tellitson" }, "");

 

            Assert.AreEqual("Mary Tellitson", commandAdapter.GetFieldValue("Full Name"));

            Assert.AreEqual("Development Department", commandAdapter.GetFieldValue("Department"));

            Assert.AreEqual("Manager", commandAdapter.GetFieldValue("Position"));

 

            commandAdapter.DoAction("Edit", null);

 

            commandAdapter.SetFieldValue("First Name", "User_1");

            commandAdapter.SetFieldValue("Last Name", "User_2");

 

            commandAdapter.SetFieldValue("Position", "Developer");

 

            commandAdapter.DoAction("Save", null);

 

            Assert.AreEqual("User_1 User_2", commandAdapter.GetFieldValue("Full Name"));

            Assert.AreEqual("Developer", commandAdapter.GetFieldValue("Position"));

        }


This example can be modified to be used with any unit-testing engine and can be extended with additional commands by introducing new methods in the TestCommandAdapter class. You can also modify the command syntax by renaming and modifying TestCommandAdapter‘s methods.
The sample application uses active directory authentication, and we kill the application instance and start a new one between tests. If you are using standard authentication, you can reuse the application instance by logging off the application instead of killing it. To do this, modify the TearDown method of the WinEasyTestFixtureHelperBase/WebEasyTestFixtureHelperBase.


Preparing your application for testing

To use the EasyTest.Tests assembly in your application, you will need to modify the TestApplcationTests.cs file and specify correct paths to the application on your local hard drive. Usually tests need particular test data. And, since they should also be atomic, we recommend you back up the test data between different test runs. For this purpose, modify the DatabaseVersionMismatch event handler as follows:

#if EASYTEST

            e.Updater.Update();

            e.Handled = true;

            TestApplication.EasyTest.InMemoryDataStoreProvider.Save();

#else

...

#endif

The ASP.NET application should restore this data when a test is finished, and so you should handle the AcquireRequestState event (See sample Global.asax)

#if EASYTEST

        protected void Application_AcquireRequestState(Object sender, EventArgs e) {

            if(HttpContext.Current.Request.Params["Reset"] == "true") {

                TestApplication.EasyTest.InMemoryDataStoreProvider.Reload();

                WebApplication.Instance.LogOff();

                WebApplication.Redirect(Request.RawUrl.Replace("&Reset=true", "").Replace("?Reset=true", ""), true);

            }

        }

#endif

We also recommend you reload the data store provider in the Session_Start event handler:

        protected void Session_Start(Object sender, EventArgs e) {

#if EASYTEST

            TestApplication.EasyTest.InMemoryDataStoreProvider.Reload();

#endif

            WebApplication application = new TestApplicationAspNetApplication();

            WebApplication.SetInstance(Session, application);

 

#if EASYTEST

            application.ConnectionString = "XpoProvider=InMemoryDataSet";

#else

            if(ConfigurationManager.ConnectionStrings["ConnectionString"] != null) {

                WebApplication.Instance.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

            }

#endif

            WebApplication.Instance.Setup();

            WebApplication.Instance.Start();

        }

 

The following steps are required for a Windows Forms application, too (Program.cs file).
a) Register the data store provider by calling the Register method of the InMemoryDataStoreProvider class:

        [STAThread]

        static void Main() {

#if EASYTEST

            DevExpress.ExpressApp.EasyTest.WinAdapter.RemotingRegistration.Register(4100);

            TestApplication.EasyTest.InMemoryDataStoreProvider.Register();

#endif

 
b) Modify the application connection string to use the in-memory data store provider:

#if EASYTEST

            winApplication.ConnectionString = "XpoProvider=InMemoryDataSet";

#else

            if(ConfigurationManager.ConnectionStrings["ConnectionString"] != null) {

                winApplication.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

            }

#endif

That was a full description of how the sample is designed. We would like to hear your thoughts about this approach for testing XAF applications.

Happy EasyTesting!

EasyTestsViaNUnit.zip

4 comment(s)
mali
mali

Can you please upgrade this code into latest xaf version

25 January, 2019
Manuel Grundner [DevExpress MVP]
Manuel Grundner [DevExpress MVP]

@mali I'm currently working on a blogpost describing this in detail. Follow https://blog.delegate.at for more information on this topic. I try to get out the blog post the next days. 

26 July, 2019
Manuel Grundner [DevExpress MVP]
Manuel Grundner [DevExpress MVP]

@mali: https://github.com/biohazard999/XafEasyTestInCodeNUnit The blog post is comming in a few days.

6 August, 2019
Manuel Grundner [DevExpress MVP]
Manuel Grundner [DevExpress MVP]
@mali: Here is the blog post https://blog.delegate.at/2019/08/08/t-is-for-testing-xaf-xpo-functional-tests-3.html
8 August, 2019

Please login or register to post comments.