5.11. Testing JavaScript

JavaScript has always been an important aspect of web development, but in the past few years developers have been creating more JavaScript code than ever before. When the term AJAX (Asynchronous JavaScript and XML) became a buzz word, developers found themselves having to learn exactly how JavaScript worked. Many new frameworks, such as jQuery and Prototype, were introduced to help ease the pain that developers were feeling when trying to create JavaScript. For some reason JavaScript has received a bad reputation. Many developers complain that it is difficult to write and hard to maintain, but in reality, if written correctly, JavaScript can be a very useful and powerful language.

Many developers are not even aware of the different testing frameworks that exist, let alone that many developers are creating their JavaScript using TDD methods. As with server-side code, many of the SOLID principles discussed in Chapter 2 apply to JavaScript. Much of the JavaScript that you see from day-to-day is contained in the HTML that calls the script. When JavaScript is organized in this way, it is very difficult to test. Some web developers complain that having an HTML page that makes many requests to the server to download multiple JavaScript files slows down the web page. They are correct, but how much does it actually slow down the web page? You'll explore this topic in detail in Chapter 8, but for now you should focus on creating code that is testable.

Currently there are a few different JavaScript testing frameworks out there, such as qUnit, Screw Unit, and jsUnit. Each framework is a bit different, but they all accomplish the same thing: an interface for testing JavaScript. Just as with the different xUnit frameworks, we'll recommend one, but it's up to you to explore the others and find which one works the best for you.

For the following examples, you'll be using qUnit. qUnit is the JavaScript testing framework used by the jQuery team to ensure their framework is working correctly. jQuery is an open source JavaScript framework that makes working with JavaScript a breeze.

The following example is an HTML page that creates two instances of the account JavaScript object and transfers funds between them. After the funds have been transferred, the balances of each account are displayed in a span tag. The span text is set using the jQuery JavaScript library:

<body>
    <script type="text/javascript">
        var checkingAccount = new Account(564858510, 600);
        var savingsAccount = new Account(564858507, 100);

        savingsAccount.Transfer(checkingAccount, 100);

        jQuery("document").ready(function() {

            jQuery("#SavingsBalance").text(savingsAccount.Balance);
            jQuery("#CheckingBalance").text(checkingAccount.Balance);
        });
    </script>

    <div>
        Savings Balance:<span id="SavingsBalance"></span>
    </div>

    <div>
        Checking Balance:<span id="CheckingBalance"></span>
    </div>
</body>

Because the account logic object logic has been abstracted from the HTML page, this logic will be easy to test using a JavaScript testing framework such as qUnit:

function Account(accountNumber, balance)
{
    this.AccountNumber = accountNumber;
    this.Balance = balance;
}
Account.prototype.Transfer = function(toAccount, amount)
{
    toAccount.Balance += amount;
    this.Balance = this.Balance — amount;
}

Figure 5-24 shows an example project with the qUnit tests added. The testing framework and unit tests are contained in the SimpleqUnit.Tests.Javascript.Unit directory. The qUnit framework requires the testrunner.js file and qUnit.css file to be included. In this example your unit tests for the account JavaScript object are contained in the AccountTests.htm file. With this structure, you can think of the AccountTests.htm file as the test fixture. The unit tests are written in JavaScript and are contained inside a script tag in the AccountTests.htm file. As discussed in Chapter 2, test fixtures should contain all the logic to test the behaviors of an object.

Figure 5-24. Example project with qUnit setup

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/
TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Account Tests</title>
        <script type="text/javascript" src="./js/jquery-1.2.6.js"></script>
        <script type="text/javascript" src="./js/Account.js"></script>
        <script type="text/javascript" src="scripts/testrunner.js"></script>
        <link rel="stylesheet" href="QUnit.css" type="text/css" media="screen" />

        <script type="text/javascript">

module('When creating a new Account',
            {
                setup: function() {
                        account = new Account("12345", 100);
                    }
            });

            test('Should_Transfer_From_Checking_To_Savings_Successfully', function(){
                equals(account.Balance, 100);

                var checkingAccount = new Account(564858510, 600);
                var savingsAccount = new Account(564858507, 100);

                checkingAccount.Transfer(savingsAccount, 100);
                equals(savingsAccount.Balance, 200);
                equals(checkingAccount.Balance, 500);
            });

        </script>
    </head>
    <body>
        <h2 id="banner">Account unit tests</h2>
        <h2 id="userAgent"></h2>
        <ol id="tests"></ol>
        <div id="main"></div>
    </body>
</html>

Because the qUnit test runners are HTML files, a suite of JavaScript tests can become unwieldy very quickly, not to mention the fact that having to click through a bunch of HTML pages to see if JavaScript is working correctly isn't very automated. A pattern that has emerged involves using WatiN or Selenium to run the qUnit test runner and then parse the result looking for failures. The problem with this is that a WatiN/Selenium test would be required for each qUnit test runner file, meaning you would be doubling your testing effort having to create the qUnit test and then creating a WatiN/Selenium test to go along with it. Some unit testing frameworks support concepts called iterative tests or row tests. With iterative tests, data can be passed into a single test and assertions can be performed on the data. This works great for qUnit; you can pass in the results from multiple test runners and parse the results for errors with only having to write one WatiN/Selenium test. As of nUnit, 2.4.8 row tests/iterative tests were not supported out-of-the box, but a free add-on, aptly named Iterative Test, is available for download on the nUnit website.

The next listing shows the basics of getting started with this pattern using an iterative test using the Iterative Test nUnit plug-in and WatiN. The UnitTestsToRun function returns an IEnumerable result set containing the results from the test runner. The AccountTest function calls an extension method that verifies that the test has passed:

[TestFixture]
public class AccountTests
{
    private IE m_browser;

    [TestFixtureSetUp]
    public void TestFixtureSetUp()

{
        m_browser = new IE();
        m_browser.ClearCache();
    }

    [TestFixtureTearDown]
    public void TestFixtureTearDown()
    {
        m_browser.Close();
    }

    [IterativeTest("UnitTestsToRun")]
    public void AccountTest(object current)
    {
        ((QUnitTest)current).ShouldPass();
    }

    public IEnumerable UnitTestsToRun()
    {
        return new[]
                      {
                        "AccountTests.htm",
                        "AccountHistoryTests.htm",
                        "LoanCalculationTests.htm"
                      }.SelectMany(page => GetTestResults(page));
    }

    public IEnumerable<QUnitTest> GetTestResults(string testPage)
    {
        string testDirectory = "js/UnitTests";

        TestFixtureSetUp();
        m_browser.GoTo(string.Format("http://localhost:21401/{0}/{1}",
                                     testDirectory,testPage));

        m_browser.WaitForComplete();

        return ParseResultsFromPage(testPage);
    }

    public IEnumerable<QUnitTest> ParseResultsFromPage(string testPage) {}

    private static string ParseTestName(string testNameTag) {}
}

Using an iterative testing pattern with WatiN/Selenium allows your tests to stay organized and allows for tests to be added very easily. The preceding example shows the iterative test pattern code that is executed via nUnit in Figure 5-25. If a single test within the qUnit test runner fails, the iterative testing pattern allows you to see which test failed. Figure 5-26 shows a test failure. Pay attention to the details of any information that can be provided from parsing the results of the qUnit test runner.

Figure 5-25. A failing qUnit test

Figure 5-26. Detailed information about failing test

AJAX is a technique which allows you to submit requests back to the server without causing a full postback to happen. The result is a much better user experience as only parts of the site are updated. However, this can cause potential problems for automated testing as parts of the UI won't always be available and there will be a delay which won't be visible to the browser or the unit testing framework.

Thankfully we can still test our UI if it's making Ajax requests under the covers. In the "JQuery_AjaxExample" sample you will find two examples of how you can test your UI when the UI takes advantage of the Microsoft ASP.NET Ajax Library or JQuery. The example below describes how to test when using the Microsoft ASP.NET Ajax Library.

Figure 5-27 is how the web page looks after the Ajax request has been made. Fundamentally, the dropdown list is populated with a set of options. A user can select an option and click submit, which will make a call to the server to collect more information. When the result is returned, the details are rendered to the web page.

Taking advantage of the built in Ajax helper methods in ASP.NET MVC, the code for the page is represented next:

<h2>AjaxLibraryExample</h2>
<% using (Ajax.BeginForm("GetPassword", new AjaxOptions { UpdateTargetId =
"customerDetails" }))
{ %>
   <p>Select an item</p>
   <p><%= Html.DropDownList("id") %></p>
   <p><input type="submit" value="Submit"/></p>
<% } %>
<div id="customerDetails">
</div>

Figure 5-27. Screenshot of Ajax example

When the form is submitted, it makes a request to the GetPassword method on the HomeController object just as it would if we attempted to access a normal View page. The difference is that the method returns a PartialView. This is because we will only be rendering a partial section of the View instead of the entire page:

public ActionResult GetPassword(string id)
{
   Thread.Sleep(4000); //Demoware
   Customer customer = customers.Find(c => c.Id == int.Parse(id));
   return PartialView("CustomerDetails", customer);
}

As we have defined our UpdateTargetId, ASP.NET knows which DIV to use when rendering the CustomerDetails partial view. To simulate accessing a database, we've added a cheeky Thread.Sleep call.

In order to test that the UI and the ajax works as expected, we can approach the problem as we have described throughout the chapter. In the example below, we select User B from the dropdown list, submit the form, and verify the results are returned as we expect.

[Test]
public void Demo_using_Microsoft_Ajax()
{

using (IBrowser ie = BrowserFactory.Create(BrowserType.InternetExplorer))
  {
     ie.GoTo("http://localhost:62878/Home/AjaxLibraryExample");
     ie.SelectList(Find.ById("id")).Select("User B");
     ie.Button(Find.ByValue("Submit")).Click();
     bool contains = ie.Div(Find.ById("detail")).Text
        .Contains("Password — p@ssw0rd");
     Assert.IsTrue(contains);
  }
}

The key point here is that after submitting our form there is a 4 second delay before we can verify the contents of the web page. Thankfully WatiN is capable of handling this for us. There is a property called 'IE.Settings.WaitUntilExistsTimeOut' which defines how long WatiN should wait until the element appears. Before the timeout expires, it will keep searching the page to see if it can access the requested element. As a result, when our partial view is rendered, it will render the detail DIV; WatiN will detect that the DIV now exists, at which point it will verify the contents of the text. If the DIV doesn't appear within the timeout (by default 30 seconds) an exception is thrown and the test fails.

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

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