Getting started with SpecFlow and CodedUI

Introduction

Often when I approach a new tooling, framework or technology, I tend to be quite excited with the new possibilities or new ways of working.
Unfortunately many times I get quickly disappointed and left to a mercy of Google trying to solve some basic impediments. Obvious things are, at the beginning, not so obvious and I often do get stuck and spend a lot of time trying to solve “beginners” problems. Also, in this kind of situations I do not get to start with an optimal solution as I am eager to get going as quickly as possible and deliver a result.
In my opinion this is also the case when it comes to SpecFlow and CodedUI. In the middle of all getting started guides, there is not a single one tackling this two chaps in a proper way.
With this post I’ll try to spread my two cents on this argument.

Getting started with SpecFlow

SpecFlow is an extension for Visual Studio that gives us the possibility to write behavior specifications using Gherkin language. If the last phrase makes no sense to you, I will advise you to Google around keywords as BDD, ATDD or Specification-By-Example, before continuing reading further.
Out of the box, SpecFlow will add some new item templates to Visual Studio, which we can use for creating our feature files. Once the feature files are added and written, SpecFlow will help us create our binding classes, but even more importantly, it’s designer will create auto generated code that will be associated to our feature file.
Based on our settings, this auto generated code will defer. It will contain the necessary classes and attributes so that the content of our feature files can get executed by your testing framework of choice. SpecFlow supports several unit test framework to execute the acceptance tests, but as you can imagine, CodedUI is not one of them.
In order to make this happen we will need to extend the SpecFlow. If you search for the information on how to do that, you will quickly find that you can define a generatorProvider for your unitTestProvider. It can be a quick fix and there are plenty of articles about this approach. However, reading the SpecFlow documentation you will notice that it reports the following regarding the generatorProvider attribute in the configuration;

Obsolete, will be removed in v2.0. Use <plugins> instead.

So, we should use a plugin instead! Unfortunately if you check the documentation about the plugins you will be welcomed by the following note:

“Note: More information about plugins comes soon!”.

This is a point where this blog post will be useful. I will explain through an example many of the in’s and out’s you need to know about writing plugins for SpecFlow.

Let’s start.

CodedUI as Unit test provider

By CodedUI tests we usually intend the test type that allows us to perform UI automation. Still we need to distinguish a couple of things here. There is the API (the engine) for the UI automation and there is a “driver” that allows us executing our tests (test execution framework). Our execution engine recalls a lot a MS Test unit test engine and it does that with a reason. Underneath they are almost the same thing. This means that our UI automation that is offered by CodedUI framework is wrapped and executed by MS Test engine. Indeed all of the attributes used are the same except the CodedUITestAttribute which again on it’s own is based on TestClassExtensionAttribute, part of Microsoft.VisualStudio.TestTools.UnitTesting.
So why can’t we then just use MSTest provider in order to execute our CodedUI automation? We actually can do that, however in order to make it work properly in this case we need to manage correctly the calls to Playback.Initialize() and Playback.Cleanup() methods. This is not handy and it is given to us for free by applying the correct attribute CodedUITestAttribute on our test class.
Now this is going to be our goal, make sure that SpecFlow applies the CodedUITestAttribute on our designer auto generated class, instead of the one that is applied by using the MS Test provider, the TestClassAttribute.

Creating the SpecFlow plugin

There is not much necessary for SpecFlow to load a plugin, a class that implements IGeneratorPlugin interface, a GeneratorPluginAttribute applied on the assembly level that will mark it as SpecFlow plugin and the registration in the configuration file. In practice, we need to create a class library project and as a first thing add SpecFlow.CustomPlugin Nuget package, which on it’s own, as a prerequisite will pull down also the SpecFlow package.

Nuget_package_SpecFlow_CustomPlugin

Once this is done, you will see that we now do have all the necessary references added to our project. We can now rename our only class (that was added by default and it’s named Class1) to CodedUIProviderPlugin and implement the IGeneratorPlugin interface (part of TechTalk.SpecFlow.Generator.Plugins namespace).
You can see that three methods are brought in by the IGeneratorPlugin interface and these method names are self explanatory; still let’s see what they should be used for:

  • RegisterConfigurationDefaults – If you are planning to intervene at the SpecFlow configuration, this is the right place to get started.
  • RegisterCustomizations – If you are extending any of the components of SpecFlow, you can register your implementation at this stage.
  • RegisterDependencies – In case your plugin is of a complex nature and it has it’s own dependencies, this can be the right place to set your Composition Root.

Our interest will focus on RegisterCustomizations method and we will remove the exceptions that were added automatically by the Visual Studio on interface implementation.
Before we start adding any more code, let’s analyse our plan. We are going to extend MsTest2010GeneratorProvider which at the bottom is the implementation of IUnitTestGeneratorProvider interface. This will be the interface that I am going to register inside the RegisterCustomizations method and assign it to the implementation class that I will name CodedUIGeneratorProvider.
At the end, our plugin class will look like this:

public class CodedUIProviderPlugin : IGeneratorPlugin
{
    public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
    {
        string unitTestProviderName = generatorConfiguration.GeneratorConfiguration.GeneratorUnitTestProvider;

        if (unitTestProviderName.Equals("mstest", StringComparison.InvariantCultureIgnoreCase) ||
            unitTestProviderName.Equals("mstest.2010", StringComparison.InvariantCultureIgnoreCase))
        {
            container.RegisterTypeAs();
        }
    }
        
    public void RegisterDependencies(ObjectContainer container)
    { }

    public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
    { }
}

As you can see, I’m checking if the currently selected generation provider is MsTest provider and if so, I am registering my custom type for that given interface. This approach is not rock solid, but it works. For example you cold resolve this interface in the container and check if the returned object is of MsTest2010GeneratorProvider type, then preform the registration, but I am happy with the current solution.
Before I forget I will mark our assembly with the necessary attribute. To do so, open the AssemblyInfo.cs file and add the following attribute:

[assembly: GeneratorPlugin(typeof(CodedUIProviderPlugin))]

All of examples and techniques are valid for SpecFlow 1.9.0. For other versions the procedures may defer.

Extending MsTest2010GeneratorProvider

Instead of implementing the IUnitTestGeneratorProvider we will just slightly alter the MsTest2010GeneratorProvider. This is possible an advised because, as I mentioned above, the test execution framework of CodedUI and MS Test deffer only in one point. MsTest2010GeneratorProvider is the default MS Test provider for SpecFlow and we have a chance to override several methods in order to change/extend the behavior.
In particular I’m interested in SetTestClass method as it is responsible for generating the code of the test class. Before proceeding I need to mention a couple of things on code generation used by SpecFlow. SpecFlow leverages the CodeDom for the necessary code generation.
CodeDom or Code Document Object Model, is a set of classes part of the .Net framework itself and makes possible generation of the source code for .NET languages without knowing the target language beforehand. It’s structure and syntax can be a bit scary at the first sight but after a while you get used to it. There are a couple of concepts you will need to master before being able to understand and work with it, that’s why I encourage you to check some guidelines on web before attempting major changes. The following is one of the posts that may help – “Using CodeDOM to generate CSharp (C#) and VB code“.

Let’s get back on overriding our SetTestClass method and check my full implementation.

public class CodedUIGeneratorProvider : MsTest2010GeneratorProvider
{
    public CodedUIGeneratorProvider(CodeDomHelper codeDomHelper)
        : base(codeDomHelper)
    { }

    public override void SetTestClass(TestClassGenerationContext generationContext, string featureTitle, string featureDescription)
    {
        base.SetTestClass(generationContext, featureTitle, featureDescription);

        foreach (CodeAttributeDeclaration declaration in generationContext.TestClass.CustomAttributes)
        {
            if (declaration.Name == "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute")
            {
                generationContext.TestClass.CustomAttributes.Remove(declaration);
                break;
            }
        }

        generationContext.TestClass.CustomAttributes.Add(
            new CodeAttributeDeclaration(
                new CodeTypeReference("Microsoft.VisualStudio.TestTools.UITesting.CodedUITestAttribute")));
    }
}

As you can see, as a first thing we are calling the base class implementation of the method so it can work it’s magic. Once the test method is setup, we will iterate through all of the attributes applied on that class and search the one that we are interested in, called TestClassAttribute. Once we found it, we will remove it, as it is going to be replaced with the CodedUITestAttribute in the following step. Also, we can stop cycling our loop as there can be only one occurrence of it. As just mentioned, the next and the last step is to add a new custom attribute on that class, which happens to be CodedUI’s, CodedUITestAttribute.

Job done!

Finishing touches

Before we build our new plugin and start using it, we need to take care of a small SpecFlow problem. In order for SpecFlow to be able to load our plugin, we need to name our assembly in a certain way. I checked the SpecFlow source code once I was unable to make it load and I discovered the following:

public class RuntimePluginLoader : IRuntimePluginLoader
{
	private const string ASSEMBLY_NAME_PATTERN = "{0}.SpecFlowPlugin";

	public IRuntimePlugin LoadPlugin(PluginDescriptor pluginDescriptor)
	{
		var assemblyName = string.Format(ASSEMBLY_NAME_PATTERN, pluginDescriptor.Name);
...

The above example is part of RuntimePluginLoader class, the implementation of the interface IRuntimePluginLoader that is used to load all of the plugins.
As you can see from the code sample it always searches for the assembly name that is composed from the given name in the app.config file plus the suffix “.SpecFlowPlugin”. This means that we need to add that suffix to our assembly in order to be found by SpecFlow loader. Let’s do so:

Assembly_Name_With_Suffix

As shown in the picture above, just add the required suffix to your assembly name in the project properties.

This is very important tip, as if you do not apply this suffix, SpecFlow will not be able to load your plugin!

Now you can compile your library and start using it in your SpecFlow projects.

Practical example

In this small example I will implement, by using CodedUI, a simple scenario (that is similar to the SpecFlow default example in it’s feature item template) of testing Windows calculator.
Once I added the necessary, I will use the plugin we just created and verify that the result is as expected. For completeness I will also implement all steps of our scenario.

Make sure you have installed SpecFlow Visual Studio Extension before continuing.

At this point we need to create a new project in which we will add our feature files and leverage our newly created plugin. We can start by creating a new CodedUI project and adding to that project the SpecFlow Nuget package. Right after we added our new CodedUI project we will get prompted to generate the code for our CodedUI test.

Generate_Code_For_CodedUI_Test

As we are not going to use the test recorder or import our automation from MTM, you can chose Cancel and close this dialog. Once that’s done we can also remove the CodedUITest1.cs file that will not be necessary. As last thing add the SpecFlow Nuget package.

Once this is in place, we can add our feature file.

Adding_Feature_File

I will name our feature file, Add.feature. For this example purposes the default scenario will be just fine. But before implementing our steps, let’s check the auto generated code for the scenario in question.

Inside the solution explorer, expand your features node and select the proposed class.

Solution_Explorer_FeatureFile_Designer_Code

The code that is presented should be similar to the following:

#region Designer generated code
#pragma warning disable
namespace SpecFlowWithCodedUI
{
    using TechTalk.SpecFlow;
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
    [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [NUnit.Framework.TestFixtureAttribute()]
    [NUnit.Framework.DescriptionAttribute("Add")]
    public partial class AddFeature
    {
        
        private static TechTalk.SpecFlow.ITestRunner testRunner;
        
#line 1 "Add.feature"
#line hidden
...

From this you can see that by default, SpecFlow used the NUnit provider to generate our tests. We can change this by indicating the desired provider inside the configuration file (App.config file which is already created by the Nuget package we previously added), open it and add the following:



  
    

As soon as you save your app.config file you will be presented with the following dialog:

SpecFlow_Configuration_Change

Answer affirmatively to this dialog and let’s re-check our designer generated code. If all went well, you should see the following:

#region Designer generated code
#pragma warning disable
namespace SpecFlowWithCodedUI
{
    using TechTalk.SpecFlow;
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
    [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()]
    public partial class AddFeature
    {
        
        private static TechTalk.SpecFlow.ITestRunner testRunner;
        
#line 1 "Add.feature"
#line hidden
...

As you can see the class structure changed and the correct attributes (targeting MS Test) are now used. This means it’s time to start taking advantage of our plugin. I will create a folder on the project level, in which I will place the compiled dll of our previously created library, and I will call it bin.

Once the library is there we need to modify our app.config file in the following way:



  
    

Note that the name we used for specifying our plugin defers from the assembly name. This is because of the suffix convention I mentioned earlier. SpecFlow plugin loader will automatically suffix the assembly name with “.SpecFlowPlugin” and then your assembly name will match and will be picked up.
Last thing I want to mention is the path. When it comes to paths, SpecFlow always starts at the project level, so we need to make a step up and get inside the bin folder. The same applies in case you placed your plugin dll in another folder.
Another handy tip about path is about the SpecFlow dependencies. In case you added (as we did) SpecFlow through Nuget package, you do not need to worry as all of the dependencies will be there and will be picked up automatically. In case you do add SpecFlow manually (no Nuget) you will need to provide the generator path in the configuration file and it should look, at example, like this:

...

  
...

After all our hard work, if we now check our designer generated code we should see the following:

namespace SpecFlowWithCodedUI
{
    using TechTalk.SpecFlow;
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
    [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [Microsoft.VisualStudio.TestTools.UITesting.CodedUITestAttribute()]
    public partial class AddFeature
    {
...

As you can see, we achieved the desired result, the CodedUITestAttribute is correctly applied on our feature (test) class.

Again, job done!

CodedUI automation in binding class

We have already created our feature file which contains a scenario. I will now write the necessary code to implement the automation of our steps.
I am not interested to show and explain all of the details of creating a binding class in SpecFlow. This is why I will move quickly through the process till the effective steps implementation. In case you are interested in details check the SpecFlow Getting Started guide.

Before we start, a slightly change is necessary, in order to make our scenario usable with windows calculator:
Open the Add.feature file and adapt the scenario so it matches the following:

Scenario: Add two numbers
	Given I have entered 50 into the calculator
	And I press add
	And I have entered 70 into the calculator
	When I press enter
	Then the result should be 120 on the screen

Then right click inside the editor and from the menu choose Generate Steps Definition:

Generate_Steps_Definition

Once you selected it you will be presented with the following screen. Leave everything defaulted and press generate.

Generate_Steps_Definition_Skeleton

You will be prompted to save this file; do so and place it in the current projects directory. Once done open the file for editing and add the following:

[Binding]
public class AddSteps
{
    private readonly ApplicationUnderTest _aut;

    public AddSteps()
    {
        _aut = ApplicationUnderTest.Launch(@"C:\Windows\System32\calc.exe");            
    }

    [Given(@"I have entered (.*) into the calculator")]
    public void GivenIHaveEnteredIntoTheCalculator(int p0)
    {
        Keyboard.SendKeys(_aut, NumbersToSendKeysString(p0));
    }

    [Given(@"I press add")]
    public void GivenIPressAdd()
    {
        Keyboard.SendKeys(_aut, "{Add}");
    }

    [When(@"I press enter")]
    public void WhenIPressEnter()
    {
        Keyboard.SendKeys(_aut, "{Enter}");
    }
        
    [Then(@"the result should be (.*) on the screen")]
    public void ThenTheResultShouldBeOnTheScreen(int result)
    {
        WinText resultTextBox = new WinText(_aut);
        resultTextBox.SearchProperties[UITestControl.PropertyNames.Name] = "Result";

        Assert.AreEqual(result.ToString(CultureInfo.InvariantCulture), resultTextBox.DisplayText);
    }

    protected string NumbersToSendKeysString(int number)
    {
        StringBuilder result = new StringBuilder();
        char[] numbers = number.ToString(CultureInfo.InvariantCulture).ToCharArray();

        foreach (char c in numbers)
        {
            result.AppendFormat("{{NumPad{0}}}", c);
        }

        return result.ToString();
    }
}

That was about it. Each time the AddSteps class is created (on every scenario run), a new instance of calculator is started. This behavior can be changed and we can start it if necessary, once per feature file or once per test run. I will try to cover this argument in detail in the future posts.
Every step execution will then perform a certain action helped by the automation offered by CodedUI framework.
At the end, we will assert that displayed value equals our expected value, expected result.

Now we can run our test and it should succeed.

Test_Explorer_Test_Run

It’s now on you to continue writing your scenarios and implement steps via CodedUI. All the necessary infrastructure is in place.

Final word

Obviously this is a simplified example. In a real world scenario in your binding classes you will use a POM (Page Object Model), a facade that hides the automation complexity and implementation details. Also, reuse of the application under test should be possible and is usually handled in a base class that can be used inherited by our binding class.

Generation providers are only one of many components we can personalize or substitute. If interested in other components, I will advise you to consult the SpecFlow source code on GitHub.

I attached both projects, the plugin and the example, in two separate downloads, you can find them both here:

During the migration of my blog from the WordPress.org on a self hosted instance, files in question got lost. Still I managed to find Plugin example project on my PC

Plugin example project
SpecFlow project using CodedUI automation

If you have any questions do not hesitate to ask in comments.

Happy coding!

Accepting a certificate that was not issued by a trusted certificate authority via CodedUI

Introduction

As we starve to have our testing environments as close as possible to our production environment is often the case that we also do set https on out test servers. For the security reasons, however, this are often self-signed certificates. This brings us to a small problem during our test automation.

By default we are pushed to accept the certificate that was not issued by a trusted certificate authority and we are shown the following page:

Security Certificate Page

Now, getting to click a button on a page is not a big deal. Still there is a problem, not in all environments this page is shown. Also, how do we recognize this page correctly?

Let’s check together a solution I came up with.

Implementing CertificateErrorPage

We can declare a HtmlDocument that will represent this page then as we know that this page is shown from a resource, we can used this in order to identify it.

Using a small trick, we will check if this page is shown and if so accept the certificate. In this way we will solve this problem once for all.

public class CertificateErrorPage : HtmlDocument
{
    protected const string sslResource = @"res://ieframe.dll/invalidcert.htm?SSLError=25165952#";

    private HtmlHyperlink _acceptLink;

    public CertificateErrorPage(BrowserWindow browserWindow)
        : base(browserWindow)
    {
        this.SearchProperties[HtmlDocument.PropertyNames.PageUrl] = 
            sslResource + browserWindow.Uri.AbsoluteUri;
    }

    protected HtmlHyperlink AcceptLink
    {
        get 
        {
            if (_acceptLink == null)
            {
                _acceptLink = new HtmlHyperlink(this);

                _acceptLink.SearchProperties.Add(HtmlControl.PropertyNames.Id, "overridelink");
            }

            return _acceptLink; 
        }
    }

    /// 
    /// Emulates the Click on "Continue to this website (not recommended)"
    /// link shown in the page.
    /// 
    public void AcceptCertificate()
    {
        Mouse.Click(AcceptLink);
    }

    /// 
    /// In case you do not want to continue with the security certificate that
    /// was issued by a non trusted certificate authority,
    /// you can choose to close this page.
    /// 
    public void CloseWebPage()
    {
        this.CloseWebPage();
    }
}

As I already mentioned, we know that once IE is showing the page in question, he retrieves it from a resource. The address of that resource is res://ieframe.dll/invalidcert.htm?SSLError=25165952.

We will use this information to identify our page. As you can see in our class constructor we are filtering on PageUrl parameter.

Once we got the right page, we can proceed.

The only element we are interested in this page is the “Continue to this website” link. We should search this element based on it’s ID as you may have a text that varies based on the language settings on your machine.
We are not exposing this element to the users of our class, instead we are going to expose only the actions that are available for this page. As you can see, two possible actions can come out of this page; we can accept to continue or decline and close the page.

In case we do accept the certificate, we are going to emulate a click on our previously found link, otherwise, in case of declining the certificate we will simply close the browser.

Now we need to see how to take advantage of our newly created window.

Using the code

After we launched our browser window, we just need to create the instance of our CertificateErrorPage page and pass the instance of our browser.

Now it is sufficient to verify if our page exists and if so call the AcceptCertificate() method.

Let’s see it in an example:

[TestMethod]
public void CodedUITestMethod1()
{
    BrowserWindow browserWindow = 
        BrowserWindow.Launch(new Uri(@"https://www.google.com"));

    CertificateErrorPage certificateErrorPage = 
        new CertificateErrorPage(browserWindow);

    if (certificateErrorPage.Exists)
    {
        certificateErrorPage.AcceptCertificate();
    }   
}

In the case the page is not shown, the Exists property will return false and no action will be needed. Otherwise, we will search for our hyperlink and preform a click on it.

You can now try it, instead of getting to google.com, by accessing a page that uses a certificate that was not issued by a trusted certificate authority. You will see that it works.

It’s simple and effective and I hope you liked it.

Happy coding!

Manage with CodedUI sites that are using Windows authentication

Introduction

It can happen that you have a need to automate tests via CodedUI on Web Sites that do use Windows authentication. This means that no login forms will be presented and your browser, by default, will pass the currently logged in user credentials to the IIS. This is not handy when it comes to automated tests, as for sure, you are planning to use a specific user to run your tests and not the one that is running the test agent services.
When it comes to Internet Explorer we do have an option to set, so that the browser will always request the credentials for the authentications process. You can find this setting inside the Internet Options setting panel Security Tab:

Internet Options

In most of the cases, as you probably will be running your tests inside your intranet, you are going to select the “Local Intranet” zone and edit the settings for it by clicking the “Custom Level” button. The following screen will be shown:

Security Settings - Local Internet Zone

Once you scroll to the bottom you will find the settings in question under User authentication -> Logon. By default, it is set to “Automatic logon only in intranet zone”. If we change it to “Prompt for user name and password” you will see that once we now reopen our site, a request to insert our credentials will be presented. In this way we can login wit a different user than the one we are logged in on this system.

Note that in some cases this settings can be disabled by a Group Policy. This is often the case in the larger organizations.

This is very handy and let’s see how to achieve this via our CodedUI test.

Setting the User Logon options from code

Instead of using the interface, we are going to try to set this option through the registry. This is quite handy as it can also bypass the group Policy restriction in case we do have sufficient rights. After some Googling I came across the following page Internet Explorer security zones registry entries for advanced users. As you can see from the title it enlists all of the settable options for Internet Explorer security zones via the registry entries. In between others there is the one we are interested to, precisely, “1A00 User Authentication: Logon”.
Our task is now clear, we need to open the key SoftwareMicrosoftWindowsCurrentVersionInternet SettingsZones1 under the Current User hive and change the value of 1A00 setting.
Let’s write some code that will help us with that.

First of all I am going to declare a couple of constants that will be used in our methods and a property that will expose the interested key:

private const string LocalIntranetZoneKeyPath =
	@"SoftwareMicrosoftWindowsCurrentVersionInternet SettingsZones1";
private const string LogonSettingValueName = "1A00";

private static RegistryKey _localIntranetZone;

/// 
/// Gets a key-level node in the registry regarding the ID Local Intranet Zone settings.
/// 
protected static RegistryKey LocalIntranetZone
{
	get
	{
		if (_localIntranetZone == null)
		{
			_localIntranetZone = Registry.CurrentUser.OpenSubKey(LocalIntranetZoneKeyPath, true);
		}

		return _localIntranetZone;
	}
}

To follow are the methods that will allow me to retrieve and set the correct logon settings. In order to have a cleaner and more precise overview over the possible options, I am going to create and use an enumeration in order to manipulate them. I will call my enumeration LogonSetting and it will list all of the accepted values.

public enum LogonSetting
{
    NotSet = -1,
    AutomaticallyLogonWithCurrentUsernameAndPassword = 0x00000,
    PromptForUserNameAndPassword = 0x10000,
    AutomaticLogonOnlyInTheIntranetZone = 0x20000,
    AnonymousLogon = 0x30000
}

All of the necessary information came from the MSDN document I previously mentioned.
Plus I added a NotSet value for the cases in which the value 1A00 doesn’t exists, which is plausible in certain cases.
At the end, the two methods that will set and retrieve the values in question:

/// 
/// Sets the IE Logon setting to the desired value.
/// 
/// The desired value to assign to the Logon Setting.
public static void SetLogonSettings(LogonSetting logonSetting)
{
	if (logonSetting == LogonSetting.NotSet)
	{
		LocalIntranetZone.DeleteValue(LogonSettingValueName);
	}
	else
	{
		LocalIntranetZone.SetValue(LogonSettingValueName, (int)logonSetting);
	}
}

/// 
/// Retrieves the current IE Logon setting.
/// 
public static LogonSetting GetLogonSettings()
{
	object logonSettingValue = LocalIntranetZone.GetValue(LogonSettingValueName);

	if (logonSettingValue == null)
	{
		return LogonSetting.NotSet;
	}

	return (LogonSetting)logonSettingValue;
}

This is all of the necessary code that we need to comfortably interact with this settings.
You can note that we are using an extra state in our enumerator that indicates the value not being set at all. If that is the case, once we are setting the value, we need to eventually remove it from the registry.

Using the code in CodedUI

Once we start laying down our CodedUI test code we need to choose what strategy we are going to adopt for preforming our task of changing the IE setting. There are couple of places in which we can do that, more precisely three events we can consider for this task. CoudedUI put’s on our disposition three attributes that we can use to trigger the execution of our code at certain, predetermined moment. This attributes are respectively TestInitialize, ClassInitialize and AssemblyInitialize. You can read more about this attributes at the following page Anatomy of a Unit Test. As most of you probably already came across this attributes I will not get in the details about them and I will pick the TestInitialize which runs given code before the run of each test. Based on your situation you may prefer to perform this only once per assembly or at the class level. The choice is yours and the implementation may vary based on your needs.

What we need to do before our test is executed is:

  1. Retrieve the current value of this setting, so that we can restore it once the test is done.
  2. Change the setting to the desire state.

This is how it translates to the code:

private const LogonSetting DesiredLogonSetting = LogonSetting.PromptForUserNameAndPassword;
private LogonSetting originalLogonSetting;

[TestInitialize()]
public void MyTestInitialize()
{
    originalLogonSetting = Page.GetLogonSettings();

    if (originalLogonSetting != DesiredLogonSetting)
    {
        Page.SetLogonSettings(DesiredLogonSetting);
    }
}

As you can see we are persisting the original value inside a variable on the class level by using our previously created method GetLogonSettings() then checking if perhaps it is already set to our desired value (so that we may be do not need to change it) and if not we are using our SetLogonSettings() method to set it to the desired value.

Now our browser will be set to always prompt for user name and password. The next thing is to restore the original condition. We are going to use the antagonistic attribute to TestInitialize which is called TestCleanup.

[TestCleanup()]
public void MyTestCleanup()
{
    if (originalLogonSetting != DesiredLogonSetting)
    {
        Page.SetLogonSettings(originalLogonSetting);
    }
}

Again, we check if the desired setting is not our original setting (and in that case we do not need to do nothing), otherwise we set our setting to the original value.

All done!

In the following paragraph we will see on how this works and how to authenticate via the Windows Security window.

Last missing piece

In order to intercept the Windows Security window, with whom we are going to interact and automatically provide the credentials, we need to declare it in the way that CodedUI can recognize it and map the elements we are going to interact with. To be clear which window we are speaking about, here is a picture of it.

Windows Security Window

This is the window that will be presented once the IE is asked to provide the credentials.
With the following code we will declare this window so that CodedUI is able to recognize it.

public class WindowsSecurityWindow : WinWindow
{
    private WinText uiUseanotheraccountText;
    private WinEdit uiUsernameEdit;
    private WinEdit uiPasswordEdit;
    private WinButton uiokButton;

    public WindowsSecurityWindow()
    {
        SearchProperties[PropertyNames.Name] = "Windows Security";
        SearchProperties[PropertyNames.ClassName] = "#32770";
        TechnologyName = "MSAA";
        WindowTitles.Add("Windows Security");
    }

    public WinText UseAnotherAccountText
    {
        get
        {
            if ((uiUseanotheraccountText == null))
            {
                uiUseanotheraccountText = new WinText(this);
                uiUseanotheraccountText.SearchProperties[WinText.PropertyNames.Name] = "Use another account";
            }

            return uiUseanotheraccountText;
        }
    }

    public WinEdit UsernameEdit
    {
        get
        {
            if ((uiUsernameEdit == null))
            {
                uiUsernameEdit = new WinEdit(this);
                uiUsernameEdit.SearchProperties[WinEdit.PropertyNames.Name] = "User name";
            }

            return uiUsernameEdit;
        }
    }

    public WinEdit PasswordEdit
    {
        get
        {
            if ((uiPasswordEdit == null))
            {
                uiPasswordEdit = new WinEdit(this);
                uiPasswordEdit.SearchProperties[WinEdit.PropertyNames.Name] = "Password";
            }

            return uiPasswordEdit;
        }
    }

    public WinButton OkButton
    {
        get
        {
            if ((uiokButton == null))
            {
                uiokButton = new WinButton(this);
                uiokButton.SearchProperties[WinButton.PropertyNames.Name] = "OK";
            }

            return uiokButton;
        }
    }

    public void Authenticate(string userName, string password)
    {
        if (UseAnotherAccountText.Exists)
        {
            Mouse.Click(UseAnotherAccountText);
        }

        UsernameEdit.Text = userName;
        PasswordEdit.Text = password;

        Mouse.Click(OkButton);
    }
}

Aside the standard code, you can see that we are searching for an element called “Use another account”. It can happen in certain cases that a variation of the window we saw in the previous image gets presented. The variation looks like following:

Windows Security Window Use Another Account

If this is the case we are still able to handle it correctly. We are going first to select the right tab:

Windows Security Window Use Another Account Selected

And insert the credentials as in the ordinary case (luckily element names are always called the same).
What remains is to recall it in the following way and pass in the desired credentials.

WindowsSecurityWindow windowsSecurityWindow = new WindowsSecurityWindow();
windowsSecurityWindow.Authenticate("userName", "password");

Putting all together

We saw all the pieces of the puzzle. Now let’s see how to prepare the IE, start it and authenticate. In the bottom of this post you will find a link where you can download my example project. There is an ASP.NET web site which uses Windows Authentication and relative CodedUI project which executes the test. In this way you can have the complete picture.

Our CodedUI test will state the following

[TestMethod]
public void CodedUITestMethod1()
{
    BrowserWindow.Launch(new Uri("http://localhost:59542/"));

    WindowsSecurityWindow windowsSecurityWindow = new WindowsSecurityWindow();
    windowsSecurityWindow.Authenticate(@"Home8test", "test");
}

As you can see, we are launching the browser window and pointing it to our application (make sure IIS Express is running before you do execute your test) and just specifying our WindowsSecurityWindow object. After that authenticate will kick in and all the actions will be performed as expected. Thanks to the code we wrote earlier, the browser will request the credentials to be provided and it will not try to login with our current user.

Note: In order this example to work, with IIS Express, you will need to enable windows authentication for IIS Express.

In order to enable windows authentication in IIS express open the file called applicationhost.config which is located in My DocumentsIISExpressconfig which again translated in my case is C:UsersMaiODocumentsIISExpressconfig. Once you open this file for edit and search the “windowsAuthentication enabled” string. You will land on the right spot. By default, this element is set to false. Just set it to true and you are ready to go.

This is an abstract of the final state of applicationhost.config.


...
  
...
    
      
    
...
  
...

One more thing, make sure that you create an user that you are going to use to test this scenario. Open the computer management and create a dummy user (in my case called test with a strong password equaling to test).

Computer Management

Last thing to do is to change the application web config with the right user and update the user name and credentials into your test.

You can now open your Test Explorer and hit run! You should see IE starting and pointing to your application page, authentication window popping out, credentials being passed and finally you are logged in!

That’s all folks

I hope you enjoyed reading this article and that you fancy the neat technique of managing the windows authentication. In this way you do not need to pre-prepare your clients on which you are going to execute your tests and more important you do not need to mess up wit the Credential Manager.
Stay tuned for more articles about CodedUI and testing automation.

Happy coding!

Download the complete example