Archive for the ‘Unit Testing’ Category

In this article, we are going to discuss on Web browser automation and how to leverage test cases so that a single test case can run on different type of browsers.
Here, we are focusing on Web Driver approach, where we instantiate specific browser driver and run the tests locally. There are other options like Selenium RC and Selenium Grid where the test cases can be run on a remote server and also has options like parallel processing etc.

Selenium IDE:
Selenium IDE is an extension for FireFox which can be used for recording test steps and generate test scripts. It provides different export options including NUnit test cases for .Net.
Install Selenium IDE extension for FireFox from http://www.seleniumhq.org/ and follow the instructions.

Open Selenium IDE from FireFox browser. The typical screen looks like below.

SeleniumIDE

Now click on the Red button to capture steps. Perform test steps on the web page. You could observe that each action would be captured on IDE. Now you may want to add an assertion for verifying results. For instance, we may want to verify text on particular label. Select assertText Command from Command dropdown and click on Select. Now you could select a field on the web page and when you click on a field on the page, it would be selected as Target on Selenium IDE. Enter the value as “Results” in value field on IDE. Now we are asserting against the value “Results”. If it matches, then test would succeed otherwise it would fail. For more details on capturing test steps on Selenium IDE, you may search on google or youtube for help videos.

Exporting Test Cases:
We have captured test steps using Selenium IDE and now we would export the test cases in C# .Net syntax. Selenium supports multiple formats for Exporting. However, we want to export as C# NUnit test cases in this example.
Go to File -> Export Test Case As and select C# /NUnit /Webdriver. Save file as Results.cs to local drive.

Options:
Selenium IDE provides few configuration settings which are important for generating test code. For instance, XPath selector doesn’t work on Internet Explorer or Edge browsers. So, we could tell IDE to include only specific Selectors. To do this, go to Options -> Locator Builders. Locator Builder window will be opened.  We can reorder the locator list based on our requirement.  In this example, I have moved xpath:position selector to end of the list, so that it would be the last option for IDE to use while generating test cases.

Single Test class for different browsers:
Now we have exported the test case to C# code and generated a N-Unit test case. Visual Studio has an extension called NUnit Test Adapter for N-Unit test cases. Once we install it we could run NUnit tests inside Visual Studio Test Explorer along with VS test cases.

The next step is to install web drivers for different browsers. The following nuget packages are available for this.
Selenium.WebDriver
Selenium.WebDriver.ChromeDriver
Selenium.WebDriver.IEDriver
Selenium.WebDriver.MicrosoftWebDriver
Selenium.Support

Define the Test class as generic of type IWebDriver. TestFixture attribute takes driver type as a parameter. In the test class constructor, instantiate the generic type. Depending on current type, the appropriate driver will be instantiated. We can specify different TextFixutre attributes depending on what all browsers we want to test. See the code snippet below.


[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(EdgeDriver))]
public class LoginTest<TDriver> where TDriver : IWebDriver, new()
{
        private ISelenium driver;
        private StringBuilder verificationErrors;

        [SetUp]
        public void SetupTest()
        {
            driver = new TDriver();
            baseURL = "http://localhost:33517/";
            verificationErrors = new StringBuilder();
        }

        [TearDown]
        public void TeardownTest()
        {
            try
            {
                selenium.Stop();
            }
            catch (Exception)
            {
                // Ignore errors if unable to close the browser
            }
            Assert.AreEqual("", verificationErrors.ToString());
        }

        [Test]
        public void LoginTest()
        {
            driver.FindElement(By.Id("EmailID")).Clear();
            driver.FindElement(By.Id("EmailID")).SendKeys(username);
            driver.FindElement(By.Id("Password")).Clear();
            driver.FindElement(By.Id("Password")).SendKeys(password);
            driver.FindElement(By.CssSelector("#login-form form")).Submit();

            //Assert here for success
        }
}

Once we apply TextFixtures for number of different browser drivers, we could see those many number of Tests in Visual Studio Test Explorer. In above case, there will be 4 tests with the name LoginTest. On mouse hover, each test case can be seen with different browser name.
NUnit provides setup and cleanup methods, which are applied with SetUp and TearDown attributes. These will be executed for each and every test.

Issues observed with different browsers:

  • Xpath selectors aren’t supported by IE and Edge drivers. Replace them with CSS selectors.
  • For Internet Explorer, if element.Click() desn’t work then we can use element.SendKeys(Keys.Enter) as an alternate.
  • Implement wait on Find element scenarios to address the issue of finding elements before they are loaded. See code below:

public void Login()
{
     var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
     wait.Until(ExpectedConditions.ElementIsVisible(By.Id("EmailOrAssociateID"))).Click();
}

  • Use Thread.Sleep() for scenarios where click event doesn’t actually trigger the action.
  • For Edge browser, you may have to run click command twice in some scenarios.
Advertisements

Introduction:
In this post I would like to discuss about writing Unit Tests for BLL components which uses EF for DB operations. My intension here is not to focus on writing tests for DB CRUD operations but to test business logic which involves retrieving further details from Database using EF to validate business scenarios. For these scenarios, either BLL component has to get data from Database or we need to explicitly provide some kind of sample data/objects so that validations can be done against of these objects.

Following are few of the different ways for doing this:

Using SQL Server Compact Edition(SQL CE):
SQL CE database can be created dynamically (.sdf file on disc) and data can be populated using SQL CE schema/sample data scripts. SQL Server scripts don’t work with SQL CE. Using SQL Server Compact Tool we can generate SQL CE compatible Schema/sample data scripts.
Here is the code snippet how to programmatically create CE database. System.Data.SqlServerCe reference has to be added to the project.
Connection String can be defined as shown below:

strConnString = “Data Source=” + CE_DB_FilePath_OnDisk;

SqlCeConnection objConn = null;
objConn = new SqlCeConnection(strConnString);
string[] arrCommands = strDatabaseSchema.Split(new string[] { m_strCommandSeparator }, StringSplitOptions.RemoveEmptyEntries);
SqlCeCommand objCmd = new SqlCeCommand();
objCmd.Connection = objConn;
objConn.Open();

foreach (string strCmd in arrCommands)
{
    string strTrimmedCmd = strCmd.Trim();
    if (!String.IsNullOrEmpty(strTrimmedCmd))
    {
        objCmd.CommandText = strTrimmedCmd;
        objCmd.ExecuteNonQuery();
    }
}

Once we create the CE Database, we can create context by passing connection string.

Public class LibraryContext : DbContext
{
    public LibraryContent()
           : base("LibraryDBConnection")
    {
    }
}

Connection string can be configured in .config file.

<connectionStrings>
    <add name=" LibraryDBConnection "
         providerName="System.Data.SqlServerCe.4.0"
         connectionString="Data Source=|DataDirectory|LibraryDb.sdf"/>
</connectionStrings>

Pros:
SQL CE is in memory/file IO based and doesn’t require installation of SQL. So these tests can be executed on build agents on TFS without any additional SQL pre-requisites.
It is a sample database which is a replica of production Database, so most of the business logic scenarios can be covered.

Cons:
The issue with this approach is, we can’t create single DBContext which serves both SQL Server and SQL CE using same model (.edmx) due to following reasons:
SQL server model uses “System.Data.SqlClient” provider whereas SQL CE expects “System.Data.SqlServerCe.4.0”

Provider=”System.Data.SqlClient”
ProviderManifestToken
EntitySet contains Schema=”dbo” however this is not applicable for SQL CE.
SQL CE limitations such as it doesn’t support Stored Procedures, Views, Triggers

Mocking DB context:
Using Moq framework we can have in-memory implementations for DB context and can be used for unit tests.
For example we have a simple model for Novels and Magazines. The context and interface looks like as shown below:

Public interface ILibraryContext
{
    DbSet Books { get; set;}
	DbSet Magazines { get; set;}
	void SaveChanges();
}

Public class LibraryContext : DbContext, ILibraryContext
{
	Public DbSet Books { get; set;}
	Public DbSet Magazines { get; set;}
	Public void SaveChanges()
    {
        //…
    }
}

Now we can mock the context.

_context = new Mock< ILibraryContext>(MockBehavior.Strict);

We need to Setup the methods which are used while testing the actual methods.

_context.Setup(c => c.SaveChanges()).Returns(LocalSaveMethod);

Implement some sample method for save.
We also need to setup objects. Objects setup requires corresponding object data.

_bookData = new List
            {
            new Book(), new Book()
};
_context.Setup(c => c. Books).Returns(PopulateBooksMockSet);

Private IDbSet PopulateBooksMockSet()
{
    Mock<IDbSet> _booksMockSet = new Mock<IDbSet>();
    _ booksMockSet.As().Setup(
        m => m.Provider).Returns(_bookData.AsQueryable().Provider);

    _ booksMockSet.As<IQueryable>().Setup(
        m => m.Expression).Returns(__bookData.AsQueryable().Expression);

    _ booksMockSet.As<IQueryable>().Setup(
        m => m.ElementType).Returns(__bookData.AsQueryable().ElementType);

    _ booksMockSet.As<IQueryable>().Setup(
        m => m.GetEnumerator()).Returns(__bookData.AsQueryable().GetEnumerator());
    return _ booksMockSet.Object;
}

Say the following is the BLL method which we wanted to test.


public void UpdateBookName(ILibraryContext context, Book bookObj, string newName)
{
    var bookInstance = context.Books.Where(b => b.Id == bookObj.Id).FirstOrDefault();

    if (bookInstance != null)
    {
        bookInstance.Name = newName;
        context.Books.Attach(bookInstance);
        context.SaveChanges();
    }

}

The typical test case would look like this:


[TestMethod]
public void UpdateName_Test()
{
     string newName = "Updated name";
     UpdateBookName(context, bookObj, newName);

     Assert.AreEqual(bookObj.Name, newName, "Name not updated");
}

Now how do we test delete object scenarios.
Generally, when we call context.Remove(object), the object doesn’t get removed from collection instead the object state will be changed to deleted state. When we call context.SaveChanges(), the ojects which are in deleted state will get removed/deleted. With mocking context, saveChanges is a dummy operation.
To test this scenario, we can use Mock<IDbSet<>>. Using Mock sets, We can check if a method was called with a particular parameter or not. We can also check how many times it was called.

bookMockSet.Verify(m => m.Remove(deletedBook), Times.Once(), “Book is not deleted”)

Pros:
Mocking DbContext works with in memory objects hence it would be faster.
It doesn’t have any addition overhead such as installation/setup etc.

Cons:
Though we have in memory context it is not equal to DbContext. It uses “LINQ to objects” compared to “LINQ to Entities” which EF actually does.
Creating mock objects (sample data/objects) is tedious.
Though it has such limitations, it can provide a good level of unit test coverage for EF.

Using SQL Express instance:
Create a separate Database programmatically on SQL express instance and populate sample data for every time when a test is executed. Delete the Data at the end of Test execution, so that the data can be repopulated next time when another test is executed. This is more like an integration testing than unit testing.

Pros:
It is a full blown end to end testing.
It doesn’t have any other maintenance overhead such as creating sample shema/data

Cons:
This makes SQL express installation as pre-requisite for build agents on TFS.

Please provide your valuable feedback/comments/suggestions that will help me improve my writing.