Automated UI testing in ASP.NET MVC5 using Selenium WebDriver

By Piotr on (tags: asp.net 4, mvc 5, Unit test, categories: code)

I would like to show you how easy it is to use unit test in Visual Studio and Selenium Web Driver to make automated UI test.

The Plan

The plan is simple: host your app, investigate given page, make assertions. The first problem is how to host our application from inside a unit test? I want to host app using current code not from external source which is already delopyed – post deployment testing. To do that we can use IIS Express…

IIS Express

Knowing that IIS Express can be run from command line:

iisexpress.exe /path:"<path to my application directory>" /port:<port number>
 
...we can use that to run our app befor all tests and close it after. Below is abstract class that do exactly that:
   1: [TestClass]
   2: public abstract class IISServerTest
   3: {
   4:     const int IisPort = 2020;
   5:     private readonly string _applicationName;
   6:     private Process _iisProcess;
   7:  
   8:     protected IISServerTest(string applicationName)
   9:     {
  10:         _applicationName = applicationName;
  11:     }
  12:  
  13:     [TestInitialize]
  14:     public void TestInitialize()
  15:     {
  16:         // Start IISExpress
  17:         StartIIS();
  18:  
  19:         Initialize();
  20:     }
  21:     
  22:     [TestCleanup]
  23:     public void TestCleanup()
  24:     {
  25:         Cleanup();
  26:         // Ensure IISExpress is stopped
  27:         if (_iisProcess.HasExited == false)
  28:         {
  29:             _iisProcess.Kill();
  30:         }
  31:     }
  32:  
  33:     public abstract void Initialize();
  34:     public abstract void Cleanup();
  35:  
  36:     private void StartIIS()
  37:     {
  38:         var applicationPath = GetApplicationPath(_applicationName);
  39:         var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
  40:  
  41:         _iisProcess = new Process();
  42:         _iisProcess.StartInfo.FileName = programFiles + @"\IIS Express\iisexpress.exe";
  43:         _iisProcess.StartInfo.Arguments = string.Format("/path:\"{0}\" /port:{1}", applicationPath, IisPort);
  44:         _iisProcess.Start();
  45:     }
  46:  
  47:     protected virtual string GetApplicationPath(string applicationName)
  48:     {
  49:         var solutionFolder = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)));
  50:         return Path.Combine(solutionFolder, applicationName);
  51:     }
  52:  
  53:     public string GetAbsoluteUrl(string relativeUrl)
  54:     {
  55:         if (!relativeUrl.StartsWith("/"))
  56:         {
  57:             relativeUrl = "/" + relativeUrl;
  58:         }
  59:         return String.Format("http://localhost:{0}/{1}", IisPort, relativeUrl);
  60:     }
  61: }

This abstract class will help us to to run our application before all tests methods. It holds reference to IIS Express process and has two methods: GetApplicationPath and GetAbsoluteUrl.  GetApplicationPath method return absolute path to our application - based on given project name. GetAbsoluteUrl method is only a helper that make absolute path from relative path.

Selenium WebDriver

Another cool tool is Selenium WebDriver. We can use that tool to communicate  with web browser and investigate given page. We can fill a form, click on links or try to find an elelemnts simply trough id or XPath query. Below is yet another abstract class that inherits from IISExpressTest and is responsible for launching a web browser. In this example I used Chrome driver but you can use IE or Firefox:

   1: public abstract class SeleniumTest : IISServerTest
   2: {
   3:     private RemoteWebDriver _remoteWebDriver;
   4:     protected IWebDriver WebDriver {
   5:         get { return _remoteWebDriver;}
   6:     }
   7:  
   8:     protected SeleniumTest(string applicationName) : base(applicationName)
   9:     {
  10:     }
  11:  
  12:     public override void Initialize()
  13:     {
  14:         _remoteWebDriver = new ChromeDriver();
  15:     }
  16:     
  17:     public override void Cleanup()
  18:     {
  19:        _remoteWebDriver.Dispose();
  20:     }
  21: }

As you can see its very simple class that is only responsible for launching and disposing the WebDriver object. WebDriver allows you to send commands to the web browser such as read DOM elements, send text to input fields, perform clicks and much more!

Example

Here is example how to use above classes:

   1: [TestClass]
   2: public class UnitTest1: SeleniumTest
   3: {
   4:     public UnitTest1() : base("weblements.www.portal")
   5:     {
   6:     }
   7:  
   8:     [TestMethod]
   9:     public void TestMethod1()
  10:     {
  11:         // Navigate to main page
  12:         WebDriver.Navigate().GoToUrl(GetAbsoluteUrl(@"home/index"));
  13:  
  14:         // Find all references to form fields
  15:         IWebElement userName        = WebDriver.FindElement(new ByIdOrName("UserName"));
  16:         IWebElement password        = WebDriver.FindElement(new ByIdOrName("Password"));
  17:         IWebElement submitButton    = WebDriver.FindElement( By.XPath("//p[class='submit']/input[1]"));
  18:  
  19:         // Fill the form and click submit button
  20:         userName.SendKeys("admin");
  21:         password.SendKeys("mysecret");
  22:         submitButton.Click();
  23:  
  24:         // Find element that confirms successful login
  25:         IWebElement loggedUserName = WebDriver.FindElement(new ByIdOrName("welcome"));
  26:  
  27:         // Assertion
  28:         Assert.AreEqual("admin", loggedUserName.Text);
  29:     }
  30: }

Above code will navigate to the main page and will try to login by filling a login form. Next, just to be sure, we find element that holds user name which confirms successful login.

The only thing you should do is to create a subclass of SeleniumTest, provide name of app we would like to test and write a test methods. Thats it! Uśmiech