5.9. More WatiN Examples

The previous section should have provided you with an insight into how you can successfully structure and get started writing your UI automation tests.

Following this, we wanted to provide some more examples for UI tests around the sample application and show how you can structure tests around an application.

Depending on how your application is structured will depend on how you need to test certain parts. With this sample application, there is a linear approach to how users would walk though the application. As such, this is how the tests need to be performed. In other applications, you could go directly to particular parts of the application by using the appropriate URL and begin testing from there.

Based on how the application is structured, there still needs to be a product in the database for us to work with. If we wanted to verify how the shopping cart behaves, then we first need to add an item to the shopping cart. This is one example of why having a centralized Product Builder allows us to reuse the logic and structure for inserting our test data, as shown below.

public override void DataCreation()
{
    Product product = new ProductBuilder()
        .WithName("p1")
        .WithDescription("d1")
        .InCategory("category")

.WithBasePrice(1.00);

    ProductRepository repository = new ProductRepository(DbConfig);
    repository.SaveOrUpdate(product);
}

With the test data in place, we can reuse our ProductsMenuPage model and abstraction to control and interact with the page. Because we have this in place, adding an item to the cart is simply two lines of test code.

[Test]
public void After_Listing_Products_Can_Add_Them_Into_Your_Shopping_Cart()
{
    ProductsMenuPage page = new ProductsMenuPage(Browser);
    ShoppingCartPage cart = page.AddToBasket("p1");

    List<ShoppingCartPage.Cart.Item> lines = cart.Items.Lines;
    lines.Count.ShouldEqual(1);
    ShoppingCartPage.Cart.Item line = lines[0];

    line.Name.ShouldEqual("p1");
    line.Price.ShouldEqual("$1");
    line.Quantity.ShouldEqual("1");
}

Once we are on the shopping cart page, with the abstraction in place you can use the list of products in the cart as a simple collection. This means you can work with it in a very easy fashion to verify everything is correct. This is the advantage of abstracting.

The ShoppingCartPage code is complex; however, this doesn't affect our tests. An example of this is how we gain access to the shopping cart lines on the page.

From the point of view of our tests, there is just a single property

public List<Item> Lines { get { return GetItems(); } }

However, the private method GetItems does all the hard work. This uses a RegEx to identify each row in a table within some HTML. The HTML has already been filtered down to just the particular DIV containing the cart by the code — Browser.Div(Find.ById("cart")).InnerHtml. The result is that we can work more easily with a particular subset of the page, pinpointing the part we want instead of having to work with the entire page. Being able to pinpoint particular parts in this fashion is extemely important to successfully test your UI.

private List<Item> GetItems()
{
   List<Item> items = new List<Item>();
   string regExGetRows = "<tr>(.*?)</tr>"

   Regex regex = new Regex(regExGetRows, RegexOptions.IgnoreCase | RegexOptions.
Singleline);

   int count = 0;

foreach (Match m in regex.Matches(Html)) //row. Html is the div with id of 'cart'
  {
     if(count++ == 0)
        continue; //skip first header row

        Capture capture = m.Captures[0];
        items.Add(GetItem(capture));
  }

  return items;
}

However, the GetItems method simply gets the particular row for that item. GetItem is what actually gets the data and information for that particular row. Again, we have used the GetItems method to help us obtain a more targeted section of the page to work with. In this case, our GetItem is simply interested in the row.

Once the method has the particular row, it uses RegEx again to pull out the particular column. The only complex section is identifying the text field. We need to use WatiN to give us the actual object allowing us to enter text for the quantity if required.

The trick is to find all the text boxes on the page, then by navigating the HTML you can obtain the parent. Then, based on the parent you can find the particular row. If the name of the parent of the textbox matches the row, you have found your quantity for the row. Not difficult, it just requires thinking about the problem in a slightly different fashion.

private Item GetItem(Capture capture)
{
  //Capture for three columns — Name + Qty + Price
   string regExGetColumns = "<td>(.*?)</td>";

   Regex regex = new Regex(regExGetColumns, RegexOptions.IgnoreCase);
   MatchCollection m = regex.Matches(capture.Value);

   Capture name = m[0].Groups[1].Captures[0];
   Capture price = m[2].Groups[1].Captures[0];
   TextField quantityTextBox = null;
   string quantityTextBoxID = "Quantity";
   foreach (TextField t in Browser.TextFields.Filter(Find.ById(quantityTextBoxID)))
   {
      if (t.Parent.Parent.InnerHtml.Contains(name.Value))
      {
         quantityTextBox = t;
         break;
      }
   }

   return new Item(Browser) { Name = name.Value, Price = price.Value,
QuantityTextField = quantityTextBox };
}

Once the method has found all the columns required, it returns a strongly typed object and as such our tests have a nice collection of objects which represent the shopping cart items. With the TextField, because we have the actual object, we can enter text and use it in exactly the same fashion as we would with any other items.

We can now use this abstraction layer to verify that after clicking the Add to Basket link for a particular product, it does, in effect, appear in our table with the correct details.

With the test in place to verify this initial feature, we can start looking at verifying that other features are in fact working. One of those features could be verifying that the items are persisted between browser sessions. When manually testing the application you would first add an item, close the browser, open it again, visit the shopping cart website and verify that the item still existed. Automated testing is no different.

Our test will need to replicate how you would manually test and how the user will experience the use case. To close the browser you need to call Dispose, while to reopen you simply create a new instance and direct it at a particular URL:

[Test]
public void Shopping_cart_is_persisted_between_browser_sessions()
{
    ProductsMenuPage page = new ProductsMenuPage(Browser);
    page.AddToBasket("p1");

    Browser.Dispose();
    StartBrowser();

    ShoppingCartPage cart = new ShoppingCartPage(Browser);
    cart.VisitPage();
    List<ShoppingCartPage.Cart.Item> lines = cart.Items.Lines;
    lines.Count.ShouldEqual(1);
}

This test is now able to verify that the shopping cart is persisted even if the browser is closed.

Web applications also commonly need to store state and additional information about the user over a long period of time. One such example is being able to store previously used addresses for future visits. While we continue to keep opening/closing the browser as we did before, that is not the aim of the test. The aim is to verify that they appear on the page. As such, we can use the back and refresh options of a browser to simulate the effects of users navigating our site and verify that the behavior works as expected:

[Test]
public void can_enter_three_addresses_select_select_middle_address()
{
    CheckoutProduct();

    for (int i = 0; i < 3; i++)
    {
        FillOutAddress(i + " Testville", "Testers Road");

        addressPage.Proceed();

        Browser.Back();
        Browser.Refresh();
    }

    addressPage.SelectExistingAddress(2);

SummaryPage summaryPage = addressPage.UseSelectedAddress();

    Assert.IsTrue(summaryPage.Address.Contains("1 Testville"));
}
private void FillOutAddress(string address1, string address2)
{
    addressPage.StreetAddress1 = address1;
    addressPage.StreetAddress2 = address2;
    addressPage.City = "Testcity";
    addressPage.State = "TC";
    addressPage.PostCode = "12345";
}

Knowing how to simulate the user's behavior is an important aspect when creating your automated UI tests.

In a similar fashion, replicating how users will use the application, you should also consider testing for what happens when an error occurs. While we don't believe in testing every possible edge case, common errors people might hit should be tested. Within this example, a common error would be the user not logging into the site with the correct username or password. While this is an error case, its useful to have a test around to verify it works as expected.

In the application, we need to be logged in to proceed to the checkout page. In our test, we want to check that on this login page, if an incorrect usernamepassword is entered, then the user is not taken to the next page, but instead is shown an error message.

The test simply automates this logic. We attempt to access the checkout, because we know what to expect; as we have not logged in yet we can return a LoginPage object which maps to the Login Page.

Once we have the page, we can call a helper method called LogOn. All being well, this should return the AddressPage:

[Test]
        public void if_login_fails_then_it_should_display_error_message_on_page()
        {
            LoginPage loginPage = cart.Checkout();
            AddressPage page = loginPage.LogOn("test", "tester123");
            Assert.IsNull(page);

            Assert.IsTrue(Browser.Text.Contains("The username or password provided
is incorrect."));
        }

While our domain model expects to return an AddressPage object, it has also been created in such a way as to return null if the login fails. This is what allows our test to work. We expect the login to fail, and as such we expect a null object to be returned. If it has, then everything is working as we expect. Just as an added extra, we added a verification that the correct message has been returned to the user. We are going direct to the Browser object to obtain this, but we could have gone via our existing LoginPage object.

Our LogOn helper method is created as shown below. We use the URL to decide what action to take:

public AddressPage LogOn()
{

Browser.Button(Find.ByValue("Log On")).Click();
    Browser.WaitForComplete();

    if(Browser.Url.Contains("/Logon"))
        return null;

    return new AddressPage(Browser);
}

Hopefully this has given you an understanding of how to structure and develop your tests based around your own application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.182.107