I blogged about WatiN back in December. Here I will show how to use it to automate a simplified Internet Banking Simulator. Most Internet banking sites do not yet use two-factor authentication schemes, such as Barclays' PINsentry card reader system. Instead you are typically asked for a membership number and one or more pass codes. A pass code consists of a string of alphanumeric characters. When the user logs into the site they are not asked to enter the complete code. Instead they are asked to enter (usually three) characters from random positions within the code. The idea is to protect against keystroke logging programs.
Suppose a users' secret code is 373549. A typical login screen may ask the user to enter the 2nd , 4th and 5th digits. In a subsequent session it will ask for the 1st, 2nd and 6th. The three positions are randomly selected for each session.
On most sites these positions are selected as a strictly monotonically increasing sequence. In other words, they might ask you to enter the 2nd, 4th and 5th digits in that order. But some sites do not impose an increasing monotonicity. In other words, they might ask you to enter the 5th, 2nd and 4th digits in that order. In my example I will stick to a monotonically increasing sequence although it works just as well without.
In order to simulate the generation of random positions I first had to write some code to generate a subset of unique random positions from all the positions in the given secret code. The standard random number generator in .NET could not be used as is because it would potentially generate repetitions of the positions. For the purposes of this post we can take it that this problem is solved. We can then concentrate on hooking this up to ASP.NET and WatiN.
A typical Internet Banking entry screen looks like this:
The secret code is shown at the bottom for visual checking.
We wish to check that
- Entering valid entries and clicking the Next button navigates to the account page (just an empty finish page for this exercise).
- Entering invalid entries and clicking the Next button displays an error message.
To do so we create a pair of NUnit functional tests that invoke WatiN to type the entries and click the Next button for us.
Test 1 - Entering valid entries
/// <summary>
/// Enters valid entries that should display finish page.
/// </summary>
[Test]
public void EnterValidEntriesShouldDisplayFinishPage()
{
// Extract entry positions
int firstPosition;
int secondPosition;
int thirdPosition;
ExtractEntryPositions(
out firstPosition,
out secondPosition,
out thirdPosition
);
// Enter valid entries for those positions
string validFirstEntry = SecretCode[firstPosition - 1].ToString();
ie.
TextField(Find.ByName("txtFirstEntry")).
TypeText(validFirstEntry);
string validSecondEntry = SecretCode[secondPosition - 1].ToString();
ie.
TextField(Find.ByName("txtSecondEntry")).
TypeText(validSecondEntry);
string validThirdEntry = SecretCode[thirdPosition - 1].ToString();
ie.
TextField(Find.ByName("txtThirdEntry")).
TypeText(validThirdEntry);
// Click Next
ie.Button(Find.ByName("btnNext")).Click();
// Assert Finished page displays
string expectedPageName = "Finished.aspx";
Assert.IsTrue(
ie.Url.Contains(expectedPageName),
String.Format("Url should contain {0}.", expectedPageName)
);
}
Here we have just shown the first two entries. It is a snapshot of WatiN as it was typing the entries.
Test 2 - Entering invalid entries
/// <summary>
/// Enters invalid entries that should display home page and error message.
/// </summary>
[Test]
public void EnterInvalidEntriesShouldDisplayHomePageAndErrorMessage()
{
// Extract entry positions
int firstPosition;
int secondPosition;
int thirdPosition;
ExtractEntryPositions(
out firstPosition,
out secondPosition,
out thirdPosition
);
// Enter invalid entries for those positions
string invalidFirstEntry =
(SecretCode[firstPosition - 1] + 1).ToString();
ie.
TextField(Find.ByName("txtFirstEntry")).
TypeText(invalidFirstEntry);
string invalidSecondEntry =
(SecretCode[secondPosition - 1] + 1).ToString();
ie.
TextField(Find.ByName("txtSecondEntry")).
TypeText(invalidSecondEntry);
string invalidThirdEntry =
(SecretCode[thirdPosition - 1] + 1).ToString();
ie.
TextField(Find.ByName("txtThirdEntry")).
TypeText(invalidThirdEntry);
// Click Next
ie.Button(Find.ByName("btnNext")).Click();
// Assert Home page displays
string expectedPageName = "default.aspx";
Assert.IsTrue(
ie.Url.Contains(expectedPageName),
String.Format("Url should contain {0}.", expectedPageName)
);
// Assert error message displays
string expectedErrorText = "Invalid code. Please try again.";
string actualErrorText = ie.Span("CustomValidator1").Text;
Assert.AreEqual(expectedErrorText, actualErrorText);
}
In the above the relevant WatiN code is highlighted in bold. The ie variable represents the Internet Explorer object. In the NUnit test setup methods we start this up and navigate to the start page like this.
ie = new IE();
ie.GoTo(startUrl);
As you can see the WatiN API sports a fluid interface. The best way of getting up to speed quickly is to use the WatiN Test Recorder and also refer to the HTML Mapping Table. It provides a list of mappings between the HTML element code in a web page and the WatiN API.
At the moment both WatiN and WatiN Test Recorder are at version 1 for their official releases and support automation of Internet Explorer only. Both are in beta for version 2 and will support Firefox.