YUI recommends YUI3.

YUI 2 has been deprecated since 2011. This site acts as an archive for files and documentation.

This documentation is no longer maintained.

YUI 2: YUI Test

YUI 2: YUI Test

YUI Test is a testing framework for browser-based JavaScript solutions. Using YUI Test, you can easily add unit testing to your JavaScript solutions. While not a direct port from any specific xUnit framework, YUI Test does derive some characteristics from nUnit and JUnit.

YUI Test features:

  • Rapid creation of test cases through simple syntax.
  • Advanced failure detection for methods that throw errors.
  • Grouping of related test cases using test suites.
  • Asynchronous tests for testing events and Ajax communication.
  • DOM Event simulation in all A-grade browsers.

Video: Test-Driven Development with YUI Test

In this 48-minute tech talk from October 2008, Yahoo! engineer Nicholas C. Zakas introduces you to the world of test-driven development and how it can be applied to JavaScript projects using YUI Test. [iPod/iPhone compatible download also available.]

Getting Started

To use YUI Test, include the following source files in your web page:

  1. <!--CSS-->
  2. <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/logger/assets/logger.css">
  3. <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/yuitest/assets/testlogger.css">
  4.  
  5. <!-- Dependencies -->
  6. <script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
  7. <script src="http://yui.yahooapis.com/2.9.0/build/logger/logger-min.js"></script>
  8.  
  9. <!-- Source File -->
  10. <script src="http://yui.yahooapis.com/2.9.0/build/yuitest/yuitest-min.js"></script>
  11.  
<!--CSS-->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/logger/assets/logger.css">
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.9.0/build/yuitest/assets/testlogger.css">
 
<!-- Dependencies -->
<script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.9.0/build/logger/logger-min.js"></script>
 
<!-- Source File -->
<script src="http://yui.yahooapis.com/2.9.0/build/yuitest/yuitest-min.js"></script>
 

YUI dependency configurator.

YUI Dependency Configurator:

Instead of copying and pasting the filepaths above, try letting the YUI dependency Configurator determine the optimal file list for your desired components; the Configurator uses YUI Loader to write out the full HTML for including the precise files you need for your implementation.

Note: If you wish to include this component via the YUI Loader, its module name is yuitest. (Click here for the full list of module names for YUI Loader.)

Where these files come from: The files included using the text above will be served from Yahoo! servers. JavaScript files are minified, meaning that comments and white space have been removed to make them more efficient to download. To use the full, commented versions or the -debug versions of YUI JavaScript files, please download the library distribution and host the files on your own server.

Order matters: As is the case generally with JavaScript and CSS, order matters; these files should be included in the order specified above. If you include files in the wrong order, errors may result.

Using Test Cases

The basis of YUI Test is the YAHOO.tool.TestCase object. A TestCase object is created by using the YAHOO.tool.TestCase constructor and passing in an object containing methods and other information with which to initialize the test case. Typically, the argument is an object literal, for example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testSomething : function () {
  6. //...
  7. },
  8.  
  9. testSomethingElse : function () {
  10. //...
  11. }
  12. });
  13.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testSomething : function () {
        //...
    },
 
    testSomethingElse : function () {
        //...
    }
});
 

In this example, a simple test case is created named "TestCase Name". The name property is automatically applied to the test case so that it can be distinguished from other test cases that may be run during the same cycle. The two methods in this example are tests methods ( testSomething() and testSomethingElse()), which means that they are methods designed to test a specific piece of functional code. Each test method name must begin with test in order to be recognized and run as a test and each should have one or more assertions that test data for validity.

setUp() and tearDown()

As each test method is called, it may be necessary to setup information before it's run and then potentially clean up that information after the test is run. The setUp() method is run before each and every test in the test case and likewise the tearDown() method is run after each test is run. These methods should be used in conjunction to create objects before a test is run and free up memory after the test is run. For example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. //---------------------------------------------
  6. // Setup and tear down
  7. //---------------------------------------------
  8.  
  9. setUp : function () {
  10. this.data = { name : "Nicholas", age : 28 };
  11. },
  12.  
  13. tearDown : function () {
  14. delete this.data;
  15. },
  16.  
  17. //---------------------------------------------
  18. // Tests
  19. //---------------------------------------------
  20.  
  21. testName: function () {
  22. YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
  23. },
  24.  
  25. testAge: function () {
  26. YAHOO.util.Assert.areEqual(28, this.data.age, "Age should be 28");
  27. }
  28. });
  29.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Setup and tear down
    //---------------------------------------------
 
    setUp : function () {
        this.data = { name : "Nicholas", age : 28 };
    },
 
    tearDown : function () {
        delete this.data;
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------
 
    testName: function () {
        YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
    },
 
    testAge: function () {
        YAHOO.util.Assert.areEqual(28, this.data.age, "Age should be 28");
    }
});
 

In this example, a setUp() method creates a data object with some basic information. Each property of the data object is checked with a different test, testName() tests the value of data.name while testAge() tests the value of data.age. Afterwards, the data object is deleted to free up the memory. Real-world implementations will have more complex tests, of course, but they should follow the basic pattern you see in the above code.

Note: Both setUp() and tearDown() are optional methods and are only used when defined.

Ignoring Tests

There may be times when you want to ignore a test (perhaps the test is invalid for your purposes or the functionality is being re-engineered and so it shouldn't be tested at this time). To specify tests to ignore, use the _should.ignore property and name each test to skip as a property whose value is set to true:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. //---------------------------------------------
  6. // Special instructions
  7. //---------------------------------------------
  8.  
  9. _should: {
  10. ignore: {
  11. testName: true //ignore this test
  12. }
  13. },
  14.  
  15. //---------------------------------------------
  16. // Setup and tear down
  17. //---------------------------------------------
  18.  
  19. setUp : function () {
  20. this.data = { name : "Nicholas", age : 28 };
  21. },
  22.  
  23. tearDown : function () {
  24. delete this.data;
  25. },
  26.  
  27. //---------------------------------------------
  28. // Tests
  29. //---------------------------------------------
  30.  
  31. testName: function () {
  32. YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
  33. },
  34.  
  35. testAge: function () {
  36. YAHOO.util.Assert.areEqual(28, this.data.age, "Age should be 28");
  37. }
  38.  
  39. });
  40.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Special instructions
    //---------------------------------------------
 
    _should: {
        ignore: {
            testName: true //ignore this test
        }
    },
 
    //---------------------------------------------
    // Setup and tear down
    //---------------------------------------------
 
    setUp : function () {
        this.data = { name : "Nicholas", age : 28 };
    },
 
    tearDown : function () {
        delete this.data;
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------        
 
    testName: function () {
        YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
    },
 
    testAge: function () {
        YAHOO.util.Assert.areEqual(28, this.data.age, "Age should be 28");
    }
 
});
 

Here, the testName() method will be ignored when the test case is run. This is accomplished by first defining the special _should property and within it, an ignore property. The ignore property is an object containing name-value pairs representing the names of the tests to ignore. By defining a property named "testName" and setting its value to true, it says that the method named "testName" should not be executed.

Intentional Errors

There may be a time that a test throws an error that was expected. For instance, perhaps you're testing a function that should throw an error if invalid data is passed in. A thrown error in this case can signify that the test has passed. To indicate that a test should throw an error, use the _should.error property. For example:

  1. function sortArray(array) {
  2. if (array instanceof Array){
  3. array.sort();
  4. } else {
  5. throw new TypeError("Expected an array");
  6. }
  7. }
  8.  
  9. var oTestCase = new YAHOO.tool.TestCase({
  10.  
  11. name: "TestCase Name",
  12.  
  13. //---------------------------------------------
  14. // Special instructions
  15. //---------------------------------------------
  16.  
  17. _should: {
  18. error: {
  19. testSortArray: true //this test should throw an error
  20. }
  21. },
  22.  
  23. //---------------------------------------------
  24. // Tests
  25. //---------------------------------------------
  26.  
  27. testSortArray: function () {
  28. sortArray(12); //this should throw an error
  29. }
  30.  
  31. });
  32.  
function sortArray(array) {
    if (array instanceof Array){
        array.sort();
    } else {
        throw new TypeError("Expected an array");
    }
}
 
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Special instructions
    //---------------------------------------------
 
    _should: {
        error: {
            testSortArray: true //this test should throw an error
        }
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------        
 
    testSortArray: function () {
        sortArray(12);  //this should throw an error
    }
 
});
 

In this example, a test case is created to test the standalone sortArray() function, which simply accepts an array and calls its sort() method. But if the argument is not an array, an error is thrown. When testSortArray() is called, it throws an error because a number is passed into sortArray(). Since the _should.error object has a property called "testSortArray" set to true, this indicates that testSortArray() should pass only if an error is thrown.

It is possible to be more specific about the error that should be thrown. By setting a property in _should.error to a string, you can specify that only a specific error message can be construed as a passed test. Here's an example:

  1. function sortArray(array) {
  2. if (array instanceof Array){
  3. array.sort();
  4. } else {
  5. throw new TypeError("Expected an array");
  6. }
  7. }
  8.  
  9. var oTestCase = new YAHOO.tool.TestCase({
  10.  
  11. name: "TestCase Name",
  12.  
  13. //---------------------------------------------
  14. // Special instructions
  15. //---------------------------------------------
  16.  
  17. _should: {
  18. error: {
  19. testSortArray: "Expected an array"
  20. }
  21. },
  22.  
  23. //---------------------------------------------
  24. // Tests
  25. //---------------------------------------------
  26.  
  27. testSortArray: function () {
  28. sortArray(12); //this should throw an error
  29. }
  30.  
  31. });
  32.  
function sortArray(array) {
    if (array instanceof Array){
        array.sort();
    } else {
        throw new TypeError("Expected an array");
    }
}
 
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Special instructions
    //---------------------------------------------
 
    _should: {
        error: {
            testSortArray: "Expected an array"
        }
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------        
 
    testSortArray: function () {
        sortArray(12);  //this should throw an error
    }
 
});
 

In this example, the testSortArray() test will only pass if the error that is thrown has a message of "Expected an array". If a different error occurs within the course of executing testSortArray(), then the test will fail due to an unexpected error.

If you're unsure of the message but know the type of error that will be thrown, you can specify the error constructor for the error you're expecting to occur:

  1. function sortArray(array) {
  2. if (array instanceof Array){
  3. array.sort();
  4. } else {
  5. throw new TypeError("Expected an array");
  6. }
  7. }
  8.  
  9. var oTestCase = new YAHOO.tool.TestCase({
  10.  
  11. name: "TestCase Name",
  12.  
  13. //---------------------------------------------
  14. // Special instructions
  15. //---------------------------------------------
  16.  
  17. _should: {
  18. error: {
  19. testSortArray: TypeError
  20. }
  21. },
  22.  
  23. //---------------------------------------------
  24. // Tests
  25. //---------------------------------------------
  26.  
  27. testSortArray: function () {
  28. sortArray(12); //this should throw an error
  29. }
  30.  
  31. });
  32.  
function sortArray(array) {
    if (array instanceof Array){
        array.sort();
    } else {
        throw new TypeError("Expected an array");
    }
}
 
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Special instructions
    //---------------------------------------------
 
    _should: {
        error: {
            testSortArray: TypeError
        }
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------        
 
    testSortArray: function () {
        sortArray(12);  //this should throw an error
    }
 
});
 

In this example, the test will pass if a TypeError gets thrown; if any other type of error is thrown, the test will fail. A word of caution: TypeError is the most frequently thrown error by browsers, so specifying a TypeError as expected may give false passes.

To narrow the margin of error between checking for an error message and checking the error type, you can create a specific error object and set that in the _should.error property, such as:

  1. function sortArray(array) {
  2. if (array instanceof Array){
  3. array.sort();
  4. } else {
  5. throw new TypeError("Expected an array");
  6. }
  7. }
  8.  
  9. var oTestCase = new YAHOO.tool.TestCase({
  10.  
  11. name: "TestCase Name",
  12.  
  13. //---------------------------------------------
  14. // Special instructions
  15. //---------------------------------------------
  16.  
  17. _should: {
  18. error: {
  19. testSortArray: new TypeError("Expected an array")
  20. }
  21. },
  22.  
  23. //---------------------------------------------
  24. // Tests
  25. //---------------------------------------------
  26.  
  27. testSortArray: function () {
  28. sortArray(12); //this should throw an error
  29. }
  30.  
  31. });
  32.  
function sortArray(array) {
    if (array instanceof Array){
        array.sort();
    } else {
        throw new TypeError("Expected an array");
    }
}
 
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Special instructions
    //---------------------------------------------
 
    _should: {
        error: {
            testSortArray: new TypeError("Expected an array")
        }
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------        
 
    testSortArray: function () {
        sortArray(12);  //this should throw an error
    }
 
});
 

Using this code, the testSortArray() method will only pass if a TypeError object is thrown with a message of "Expected an array"; if any other type of error occurs, then the test fails due to an unexpected error.

Note: If a test is marked as expecting an error, the test will fail unless that specific error is thrown. If the test completes without an error being thrown, then it fails.

Assertions

Test methods use assertions to check the validity of a particular action or function. An assertion method tests (asserts) that a condition is valid; if not, it throws an error that causes the test to fail. If all assertions pass within a test method, it is said that the test has passed. To aid in writing tests, the YAHOO.util.Assert object contains several assertion methods that can be used to validate data.

Topics covered in this section include:

Equality Assertions

The simplest assertions are areEqual() and areNotEqual(). Both methods accept three arguments: the expected value, the actual value, and an optional failure message (a default one is generated if this argument is omitted). For example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testEqualityAsserts : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.areEqual(5, 5); //passes
  9. Assert.areEqual(5, "5"); //passes
  10. Assert.areNotEqual(5, 6); //passes
  11. Assert.areEqual(5, 6, "Five was expected."); //fails
  12. }
  13. });
  14.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testEqualityAsserts : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.areEqual(5, 5);     //passes
        Assert.areEqual(5, "5");     //passes
        Assert.areNotEqual(5, 6);  //passes
        Assert.areEqual(5, 6, "Five was expected."); //fails
    }
});
 

These methods use the double equals (==) operator to determine if two values are equal, so type coercion may occur. This means that the string "5" and the number 5 are considered equal because the double equals sign converts the number to a string before doing the comparison. If you don't want values to be converted for comparison purposes, use the sameness assertions instead.

Sameness Assertions

The sameness assertions are areSame() and areNotSame(), and these accept the same three arguments as the equality assertions: the expected value, the actual value, and an optional failure message. Unlike the equality assertions, these methods use the triple equals operator (===) for comparisions, assuring that no type coercion will occur. For example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testSamenessAsserts : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.areSame(5, 5); //passes
  9. Assert.areSame(5, "5"); //fails
  10. Assert.areNotSame(5, 6); //passes
  11. Assert.areNotSame(5, "5"); //passes
  12. Assert.areSame(5, 6, "Five was expected."); //fails
  13. }
  14. });
  15.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testSamenessAsserts : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.areSame(5, 5);      //passes
        Assert.areSame(5, "5");    //fails
        Assert.areNotSame(5, 6);   //passes
        Assert.areNotSame(5, "5"); //passes
        Assert.areSame(5, 6, "Five was expected."); //fails
    }
});
 

Note: Even though this example shows multiple assertions failing, a test will stop as soon as one assertion fails, causing all others to be skipped.

Data Type Assertions

There may be times when some data should be of a particular type. To aid in this case, there are several methods that test the data type of variables. Each of these methods accepts two arguments: the data to test and an optional failure message. The data type assertions are as follows:

  • isArray() - passes only if the value is an instance of Array.
  • isBoolean() - passes only if the value is a Boolean.
  • isFunction() - passes only if the value is a function.
  • isNumber() - passes only if the value is a number.
  • isObject() - passes only if the value is an object or a function.
  • isString() - passes only if the value is a string.

These are used as in the following example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testDataTypeAsserts : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.isString("Hello world"); //passes
  9. Assert.isNumber(1); //passes
  10. Assert.isArray([]); //passes
  11. Assert.isObject([]); //passes
  12. Assert.isFunction(function(){}); //passes
  13. Assert.isBoolean(true); //passes
  14. Assert.isObject(function(){}); //passes
  15.  
  16. Assert.isNumber("1", "Value should be a number."); //fails
  17. Assert.isString(1, "Value should be a string."); //fails
  18. }
  19. });
  20.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testDataTypeAsserts : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.isString("Hello world");     //passes
        Assert.isNumber(1);                 //passes
        Assert.isArray([]);                 //passes
        Assert.isObject([]);                //passes
        Assert.isFunction(function(){});    //passes
        Assert.isBoolean(true);             //passes
        Assert.isObject(function(){});      //passes
 
        Assert.isNumber("1", "Value should be a number.");  //fails
        Assert.isString(1, "Value should be a string.");    //fails
    }
});
 

In addition to these specific data type assertions, there are two generic data type assertions.

The isTypeOf() method tests the string returned when the typeof operator is applied to a value. This method accepts three arguments: the type that the value should be ("string", "number", "boolean", "undefined", "object", or "function"), the value to test, and an optional failure message. For example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testTypeOf : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.isTypeOf("string", "Hello world"); //passes
  9. Assert.isTypeOf("number", 1); //passes
  10. Assert.isTypeOf("boolean", true); //passes
  11. Assert.isTypeOf("number", 1.5); //passes
  12. Assert.isTypeOf("function", function(){}); //passes
  13. Assert.isTypeOf("object", {}); //passes
  14. Assert.isTypeOf("undefined", this.blah); //passes
  15.  
  16. Assert.isTypeOf("number", "Hello world", "Value should be a number."); //fails
  17.  
  18. }
  19. });
  20.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testTypeOf : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.isTypeOf("string", "Hello world");   //passes
        Assert.isTypeOf("number", 1);               //passes
        Assert.isTypeOf("boolean", true);           //passes
        Assert.isTypeOf("number", 1.5);             //passes
        Assert.isTypeOf("function", function(){});  //passes
        Assert.isTypeOf("object", {});              //passes
        Assert.isTypeOf("undefined", this.blah);    //passes
 
        Assert.isTypeOf("number", "Hello world", "Value should be a number."); //fails
 
    }
});
 

If you need to test object types instead of simple data types, you can also use the isInstanceOf() assertion, which accepts three arguments: the constructor function to test for, the value to test, and an optional failure message. This assertion uses the instanceof operator to determine if it should pass or fail. Example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testInstanceOf : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.isInstanceOf(Object, {}); //passes
  9. Assert.isInstanceOf(Array, []); //passes
  10. Assert.isInstanceOf(Object, []); //passes
  11. Assert.isInstanceOf(Function, function(){}); //passes
  12. Assert.isInstanceOf(Object, function(){}); //passes
  13.  
  14. Assert.isTypeOf(Array, {}, "Value should be an array."); //fails
  15.  
  16. }
  17. });
  18.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testInstanceOf : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.isInstanceOf(Object, {});    //passes
        Assert.isInstanceOf(Array, []);     //passes            
        Assert.isInstanceOf(Object, []);     //passes            
        Assert.isInstanceOf(Function, function(){});  //passes
        Assert.isInstanceOf(Object, function(){});  //passes
 
        Assert.isTypeOf(Array, {}, "Value should be an array."); //fails
 
    }
});
 

Special Value Assertions

There are numerous special values in JavaScript that may occur in code. These include true, false, NaN, null, and undefined. There are a number of assertions designed to test for these values specifically:

  • isFalse() - passes if the value is false.
  • isTrue() - passes if the value is true.
  • isNaN() - passes if the value is NaN.
  • isNotNaN() - passes if the value is not NaN.
  • isNull() - passes if the value is null.
  • isNotNull() - passes if the value is not null.
  • isUndefined() - passes if the value is undefined.
  • isNotUndefined() - passes if the value is not undefined.

Each of these methods accepts two arguments: the value to test and an optional failure message. All of the assertions expect the exact value (no type cohersion occurs), so for example calling isFalse(0) will fail.

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testSpecialValues : function () {
  6. var Assert = YAHOO.util.Assert;
  7.  
  8. Assert.isFalse(false); //passes
  9. Assert.isTrue(true); //passes
  10. Assert.isNaN(NaN); //passes
  11. Assert.isNaN(5 / "5"); //passes
  12. Assert.isNotNaN(5); //passes
  13. Assert.isNull(null); //passes
  14. Assert.isNotNull(undefined); //passes
  15. Assert.isUndefined(undefined); //passes
  16. Assert.isNotUndefined(null); //passes
  17.  
  18. Assert.isUndefined({}, "Value should be undefined."); //fails
  19.  
  20. }
  21. });
  22.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testSpecialValues : function () {
        var Assert = YAHOO.util.Assert;
 
        Assert.isFalse(false);      //passes
        Assert.isTrue(true);        //passes            
        Assert.isNaN(NaN);          //passes            
        Assert.isNaN(5 / "5");      //passes
        Assert.isNotNaN(5);         //passes
        Assert.isNull(null);        //passes
        Assert.isNotNull(undefined);    //passes
        Assert.isUndefined(undefined);  //passes
        Assert.isNotUndefined(null);    //passes
 
        Assert.isUndefined({}, "Value should be undefined."); //fails
 
    }
});
 

Forced Failures

While most tests fail as a result of an assertion, there may be times when you want to force a test to fail or create your own assertion method. To do this, use the fail() method to force a test method to fail immediately:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testForceFail : function () {
  6. YAHOO.util.Assert.fail(); //causes the test to fail
  7. }
  8. });
  9.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testForceFail : function () {
        YAHOO.util.Assert.fail();  //causes the test to fail
    }
});
 

In this case, the testForceFail() method does nothing but force the method to fail. Optionally, you can pass in a message to fail() which will be displayed as the failure message:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. testForceFail : function () {
  6. YAHOO.util.Assert.fail("I decided this should fail.");
  7. }
  8. });
  9.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    testForceFail : function () {
        YAHOO.util.Assert.fail("I decided this should fail.");
    }
});
 

When the failure of this method is reported, the message "I decided this should fail." will be reported.

Simulating User Actions

Simply testing function calls may not be adequate enough due to the level of interactivity provided by web applications. Since many operations require a user to initiate them, the YAHOO.util.UserAction object provides methods to simulate basic user events involving the keyboard and mouse.

Simulated events are browser-created events that, most of the time, behave exactly as user-initated events. Events bubble as they normally would and event objects are created with properties containing data about the event (sometimes these properties are browser-specific, so it's recommended that you make use of the browser-equalizing methods of YAHOO.util.Event to retrieve the appropriate values for properties such as target, relatedTarget, and charCode. All event handlers are called synchronously at each event target throughout the event's lifetime.

Mouse Events

There are seven mouse events that can be simulated using methods on UserAction:

  • click
  • dblclick
  • mousedown
  • mouseup
  • mouseover
  • mouseout
  • mousemove

Each event is fired by a corresponding method on UserAction that accepts two arguments: the target of the event and an optional object specifying additional information for the event. To simulate a click on the document's body element, for example, the following code can be used:

  1. YAHOO.util.UserAction.click(document.body);
  2.  
YAHOO.util.UserAction.click(document.body);
 

This code simulates a click with all of the default properties on the event object. To specify additional information, such as the Shift key being down, the second argument must be used and the exact DOM name for the event property specified (there is browser-normalizing logic that translates these into browser-specific properties when necessary):

  1. YAHOO.util.UserAction.click(document.body, {
  2. shiftKey: true
  3. });
  4.  
YAHOO.util.UserAction.click(document.body, {
    shiftKey: true
});
 

In this updated example, a click event is fired on the document's body while simulating that the Shift key is down.

The extra properties to specify vary depending on the event being simulated and are limited to this list:

  • detail - Indicates the number of times a button was clicked (DOM-compliant browsers only).
  • screenX/screenY - coordinates of the mouse event in relation to the entire screen (DOM-compliant browsers only).
  • clientX/clientY - coordinates of the mouse event in relation to the browser client area.
  • ctrlKey/altKey/shiftKey/metaKey - the state of the Ctrl, Alt, Shift, and Meta keys, respectively (true for down, false for up).
  • button - the button being used for the event, 0 for left (default), 1 for right, 2 for center.
  • relatedTarget - the element the mouse moved from (during a mouseover event) or to (during a mouseout event). You should use YAHOO.util.Event.getRelatedTarget() to retrieve this value from the generated event object as it will be translated into browser-specific property names as necessary.

Examples of the different methods and their extra properties:

  1. var element = document.getElementById("myDiv");
  2.  
  3. //simulate a click Alt key down
  4. YAHOO.util.UserAction.click(element, { altKey: true});
  5.  
  6. //simulate a double click with Ctrl key down
  7. YAHOO.util.UserAction.dblclick(element, { ctrlKey: true });
  8.  
  9. //simulate a mouse over
  10. YAHOO.util.UserAction.mouseover(element, { relatedTarget: document.body });
  11.  
  12. //simulate a mouse out
  13. YAHOO.util.UserAction.mouseout(element, { relatedTarget: document.body });
  14.  
  15. //simulate a mouse down at point (100,100) in the client area
  16. YAHOO.util.UserAction.mousedown(element, { clientX: 100, clientY: 100 });
  17.  
  18. //simulate a mouse up at point (100,100) in the client area
  19. YAHOO.util.UserAction.mouseup(element, { clientX: 100, clientY: 100 });
  20.  
  21. //simulate a mouse move at point (200, 200) in the client area
  22. YAHOO.util.UserAction.mousemove(document.body, { clientX: 200, clientY: 200 });
  23.  
var element = document.getElementById("myDiv");
 
//simulate a click Alt key down
YAHOO.util.UserAction.click(element, { altKey: true});
 
//simulate a double click with Ctrl key down
YAHOO.util.UserAction.dblclick(element, { ctrlKey: true });
 
//simulate a mouse over
YAHOO.util.UserAction.mouseover(element, { relatedTarget: document.body });
 
//simulate a mouse out
YAHOO.util.UserAction.mouseout(element, { relatedTarget: document.body });
 
//simulate a mouse down at point (100,100) in the client area
YAHOO.util.UserAction.mousedown(element, { clientX: 100, clientY: 100 });
 
//simulate a mouse up at point (100,100) in the client area
YAHOO.util.UserAction.mouseup(element, { clientX: 100, clientY: 100 });
 
//simulate a mouse move at point (200, 200) in the client area
YAHOO.util.UserAction.mousemove(document.body, { clientX: 200, clientY: 200 });
 

Key Events

There are three key event simulations available using UserAction:

  • keyup
  • keydown
  • keypress

As with the mouse events, each key event has a corresponding method of the same name and accepts two arguments, which are the target of the event and an object specifying additional properties for the event. For key events, the second argument is required as you must specify which key was interacted with. For keyup() and keydown(), the keyCode property must be specified; for keypress(), the charCode property must be included. In many cases, keyCode and charCode may be the same value to represent the same key (97, for instance, represents the "A" key as well as being the ASCII code for the letter "a"). For example:

  1. var element = document.getElementById("myDiv");
  2.  
  3. //simulate a keydown on the A key
  4. YAHOO.util.UserAction.keydown(element, { keyCode: 97 });
  5.  
  6. //simulate a keyup on the A key
  7. YAHOO.util.UserAction.keydown(element, { keyCode: 97 });
  8.  
  9. //simulate typing "a"
  10. YAHOO.util.UserAction.keypress(element, { charCode: 97 });
  11.  
var element = document.getElementById("myDiv");
 
//simulate a keydown on the A key
YAHOO.util.UserAction.keydown(element, { keyCode: 97 });
 
//simulate a keyup on the A key
YAHOO.util.UserAction.keydown(element, { keyCode: 97 });
 
//simulate typing "a"
YAHOO.util.UserAction.keypress(element, { charCode: 97 });
 

Key events also support the ctrlKey, altKey, shiftKey, and metaKey event properties.

Note: Due to differences in browser implementations, key events may not be simulated in the same manner across all browsers. For instance, when simulating a keypress event on a textbox, only Firefox will update the textbox with the new character of the key that was simulated to be pressed. For other browsers, the events are still registered and all event handlers are called, however, the textbox display and value property are not updated. These differences should go away as browser support for simulated events improves in the future.

Safari 2.x Developers: Due to a bug in the Safari 2.x codebase, simulating a keydown event may cause the browser to crash. This issue has been resolved in Safari 3.x.

Asynchronous Tests

YUI Test allows you to pause a currently running test and resume either after a set amount of time or at another designated time. The TestCase object has a method called wait(). When wait() is called, the test immediately exits (meaning that any code after that point will be ignored) and waits for a signal to resume the test.

A test may be resumed after a certain amount of time by passing in two arguments to wait(): a function to execute and the number of milliseconds to wait before executing the function (similar to using setTimeout()). The function passed in as the first argument will be executed as part of the current test (in the same scope) after the specified amount of time. For example:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. //---------------------------------------------
  6. // Setup and tear down
  7. //---------------------------------------------
  8.  
  9. setUp : function () {
  10. this.data = { name : "Nicholas", age : 29 };
  11. },
  12.  
  13. tearDown : function () {
  14. delete this.data;
  15. },
  16.  
  17. //---------------------------------------------
  18. // Tests
  19. //---------------------------------------------
  20.  
  21. testAsync: function () {
  22. YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
  23.  
  24. //wait 1000 milliseconds and then run this function
  25. this.wait(function(){
  26. YAHOO.util.Assert.areEqual(29, this.data.age, "Age should be 29");
  27.  
  28. }, 1000);
  29. }
  30. });
  31.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Setup and tear down
    //---------------------------------------------
 
    setUp : function () {
        this.data = { name : "Nicholas", age : 29 };
    },
 
    tearDown : function () {
        delete this.data;
    },
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------
 
    testAsync: function () {
        YAHOO.util.Assert.areEqual("Nicholas", this.data.name, "Name should be 'Nicholas'");
 
        //wait 1000 milliseconds and then run this function
        this.wait(function(){
            YAHOO.util.Assert.areEqual(29, this.data.age, "Age should be 29");
 
        }, 1000);
    }
});
 

In this code, the testAsync() function does one assertion, then waits 1000 milliseconds before performing another assertion. The function passed into wait() is still in the scope of the original test, so it has access to this.data just as the original part of the test does. Timed waits are helpful in situations when there are no events to indicate when the test should resume.

If you want a test to wait until a specific event occurs before resuming, the wait() method can be called without any arguments. At that point, testing will resume only when the resume() method is called. The resume() method accepts a single argument, which is a function to run when the test resumes. This function should specify additional assertions. The following tests to see if the Anim object has performed its animation completely:

  1. var oTestCase = new YAHOO.tool.TestCase({
  2.  
  3. name: "TestCase Name",
  4.  
  5. //---------------------------------------------
  6. // Tests
  7. //---------------------------------------------
  8.  
  9. testAnimation : function (){
  10. var Assert = YAHOO.util.Assert;
  11. var YUD = YAHOO.util.Dom;
  12.  
  13. //animate width to 400px
  14. var myAnim = new YAHOO.util.Anim('testDiv', { width: { to: 400 } }, 3, YAHOO.util.Easing.easeOut);
  15.  
  16. //assign oncomplete handler
  17. myAnim.onComplete.subscribe(function(){
  18.  
  19. //tell the TestRunner to resume
  20. this.resume(function(){
  21.  
  22. Assert.areEqual(YUD.get("testDiv").offsetWidth, 400, "Width of the DIV should be 400.");
  23.  
  24. });
  25.  
  26. }, this, true);
  27.  
  28. //start the animation
  29. myAnim.animate();
  30.  
  31. //wait until something happens
  32. this.wait();
  33.  
  34. }
  35. });
  36.  
var oTestCase = new YAHOO.tool.TestCase({
 
    name: "TestCase Name",
 
    //---------------------------------------------
    // Tests
    //---------------------------------------------
 
    testAnimation : function (){
        var Assert = YAHOO.util.Assert;
        var YUD = YAHOO.util.Dom;
 
        //animate width to 400px
        var myAnim = new YAHOO.util.Anim('testDiv', { width: { to: 400 } }, 3, YAHOO.util.Easing.easeOut);
 
        //assign oncomplete handler
        myAnim.onComplete.subscribe(function(){
 
            //tell the TestRunner to resume
            this.resume(function(){
 
                Assert.areEqual(YUD.get("testDiv").offsetWidth, 400, "Width of the DIV should be 400.");
 
            });
 
        }, this, true);
 
        //start the animation
        myAnim.animate();
 
        //wait until something happens
        this.wait();
 
    }
});
 

In this example, an Anim object is used to animate the width of an element to 400 pixels. When the animation is complete, the onComplete method is fired, so that is where the resume() method is called. The function passed into resume() simply tests that the final width of the element is indeed 400 pixels. Note that for this to work, the scope of the event handler had to be adjusted by passing in the second and third arguments to subscribe(). Once the event handler is set up, the animation begins and then the wait() method is called without any arguments. At that point, testing stops until the animation completes and resume() is called.

Test Suites

For large web applications, you'll probably have many test cases that should be run during a testing phase. A test suite helps to handle multiple test cases by grouping them together into functional units that can be run together. To create new test suite, use the YAHOO.tool.TestSuite constructor and pass in the name of the test suite. The name you pass in is for logging purposes and allows you to discern which TestSuite instance currently running. For example:

  1. //create the test suite
  2. var oSuite = new YAHOO.tool.TestSuite("TestSuite Name");
  3.  
  4. //add test cases
  5. oSuite.add(new YAHOO.tool.TestCase({
  6. //...
  7. }));
  8. oSuite.add(new YAHOO.tool.TestCase({
  9. //...
  10. }));
  11. oSuite.add(new YAHOO.tool.TestCase({
  12. //...
  13. }));
  14.  
//create the test suite
var oSuite = new YAHOO.tool.TestSuite("TestSuite Name");
 
//add test cases
oSuite.add(new YAHOO.tool.TestCase({
    //...
}));
oSuite.add(new YAHOO.tool.TestCase({
    //...
}));
oSuite.add(new YAHOO.tool.TestCase({
    //...
}));
 

Here, a test suite is created and three test cases are added to it using the add() method. The test suite now contains all of the information to run a series of tests.

It's also possible to add other multiple TestSuite instances together under a parent TestSuite using the same add() method:

  1. //create a test suite
  2. var oSuite = new YAHOO.tool.TestSuite("TestSuite Name");
  3.  
  4. //add a test case
  5. oSuite.add(new YAHOO.tool.TestCase({
  6. ...
  7. });
  8.  
  9. //create another suite
  10. var oAnotherSuite = new YAHOO.tool.TestSuite("test_suite_name");
  11.  
  12. //add a test case
  13. oAnotherSuite.add(new YAHOO.tool.TestCase({
  14. ...
  15. });
  16.  
  17. //add the second suite to the first
  18. oSuite.add(oAnotherSuite);
  19.  
//create a test suite
var oSuite = new YAHOO.tool.TestSuite("TestSuite Name");
 
//add a test case
oSuite.add(new YAHOO.tool.TestCase({
    ...
});
 
//create another suite
var oAnotherSuite = new YAHOO.tool.TestSuite("test_suite_name");
 
//add a test case
oAnotherSuite.add(new YAHOO.tool.TestCase({
    ...
});
 
//add the second suite to the first
oSuite.add(oAnotherSuite);
 

By grouping test suites together under a parent test suite you can more effectively manage testing of particular aspects of an application.

Test suites may also have setUp() and tearDown() methods. A test suite's setUp() method is called before the first test in the first test case is executed (prior to the test case's setUp() method); a test suite's tearDown() method executes after all tests in all test cases/suites have been executed (after the last test case's tearDown() method). To specify these methods, pass an object literal into the YAHOO.tool.TestSuite constructor:

  1. //create a test suite
  2. var oSuite = new YAHOO.tool.TestSuite({
  3. name : "TestSuite Name",
  4.  
  5. setUp : function () {
  6. //test-suite-level setup
  7. },
  8.  
  9. tearDown: function () {
  10. //test-suite-level teardown
  11. }
  12. });
  13.  
//create a test suite
var oSuite = new YAHOO.tool.TestSuite({
    name : "TestSuite Name",
 
    setUp : function () {
        //test-suite-level setup 
    },
 
    tearDown: function () {
        //test-suite-level teardown
    }
});
 

Test suite setUp() and tearDown() may be helpful in setting up global objects that are necessary for a multitude of tests and test cases.

Running Tests

In order to run test cases and test suites, use the YAHOO.tool.TestRunner object. This object is a singleton that simply runs all of the tests in test cases and suites, reporting back on passes and failures. To determine which test cases/suites will be run, add them to the TestRunner using the add() method. Then, to run the tests, call the run() method:

  1. //add the test cases and suites
  2. YAHOO.tool.TestRunner.add(oTestCase);
  3. YAHOO.tool.TestRunner.add(oTestSuite);
  4.  
  5. //run all tests
  6. YAHOO.tool.TestRunner.run();
  7.  
//add the test cases and suites
YAHOO.tool.TestRunner.add(oTestCase);
YAHOO.tool.TestRunner.add(oTestSuite);
 
//run all tests
YAHOO.tool.TestRunner.run();
 

If at some point you decide not to run the tests that have already been added to the TestRunner, they can be removed by calling clear():

  1. YAHOO.tool.TestRunner.clear();
  2.  
YAHOO.tool.TestRunner.clear();
 

Making this call removes all test cases and test suites that were added using the add() method.

TestRunner Events

The TestRunner provides results and information about the process by publishing several events. These events can occur at four different points of interest: at the test level, at the test case level, at the test suite level, and at the TestRunner level. The data available for each event depends completely on the type of event and the level at which the event occurs.

Test-Level Events

Test-level events occur during the execution of specific test methods. There are three test-level events:

  • TestRunner.TEST_PASS_EVENT - occurs when the test passes.
  • TestRunner.TEST_FAIL_EVENT - occurs when the test fails.
  • TestRunner.TEST_IGNORE_EVENT - occurs when a test is ignored.

For each of these events, the event data object has three properties:

  • type - indicates the type of event that occurred.
  • testCase - the test case that is currently being run.
  • testName - the name of the test that was just executed or ignored.

For TestRunner.TEST_FAIL_EVENT, an error property containing the error object that caused the test to fail.

TestCase-Level Events

There are two events that occur at the test case level:

  • TestRunner.TEST_CASE_BEGIN_EVENT - occurs when the test case is next to be executed but before the first test is run.
  • TestRunner.TEST_CASE_COMPLETE_EVENT - occurs when all tests in the test case have been executed or ignored.

For these two events, the event data object has three properties:

  • type - indicates the type of event that occurred.
  • testCase - the test case that is currently being run.

For TEST_CASE_COMPLETE_EVENT, an additional property called results is included. The results property is an object containing the aggregated results for all tests in the test case (it does not include information about tests that were ignored). Each test that was run has an entry in the result object where the property name is the name of the test method and the value is an object with two properties: result, which is either "pass" or "fail", and message, which is a text description of the result (simply "Test passed" when a test passed or the error message when a test fails). Additionally, the failed property indicates the number of tests that failed in the test case, the passed property indicates the number of tests that passed, and the total property indicates the total number of tests executed. A typical results object looks like this:

  1. {
  2. failed: 1,
  3. passed: 1,
  4. ignored: 0,
  5. total: 2,
  6. type: "testcase",
  7. name: "Test Case 0",
  8.  
  9. test0: {
  10. result: "pass",
  11. message: "Test passed",
  12. type: "test",
  13. name: "test0"
  14. },
  15.  
  16. test1: {
  17. result: "fail",
  18. message: "Assertion failed",
  19. type: "test",
  20. name: "test1"
  21. }
  22. }
  23.  
{
    failed: 1,
    passed: 1,
    ignored: 0,
    total: 2,
    type: "testcase",
    name: "Test Case 0",
 
    test0: {
        result: "pass",
        message: "Test passed",
        type: "test",
        name: "test0"
    },
 
    test1: {
        result: "fail",
        message: "Assertion failed",
        type: "test",
        name: "test1"
    }
}
 

The TEST_CASE_COMPLETE_EVENT provides this information for transparency into the testing process.

TestSuite-Level Events

There are two events that occur at the test suite level:

  • TestRunner.TEST_SUITE_BEGIN_EVENT - occurs when the test suite is next to be executed but before the first test is run.
  • TestRunner.TEST_SUITE_COMPLETE_EVENT - occurs when all tests in all test cases in the test suite have been executed or ignored.

For these two events, the event data object has three properties:

  • type - indicates the type of event that occurred.
  • testSuite - the test suite that is currently being run.

The TEST_SUITE_COMPLETE_EVENT also has a results property, which contains aggregated results for all of the test cases (and other test suites) it contains. Each test case and test suite contained within the main suite has an entry in the results object, forming a hierarchical structure of data. A typical results object may look like this:

  1. {
  2. failed: 2,
  3. passed: 2,
  4. ignored: 0,
  5. total: 4,
  6. type: "testsuite",
  7. name: "Test Suite 0",
  8.  
  9. testCase0: {
  10. failed: 1,
  11. passed: 1,
  12. ignored: 0,
  13. total: 2,
  14. type: "testcase",
  15. name: "testCase0",
  16.  
  17. test0: {
  18. result: "pass",
  19. message: "Test passed."
  20. type: "test",
  21. name: "test0"
  22. },
  23. test1: {
  24. result: "fail",
  25. message: "Assertion failed.",
  26. type: "test",
  27. name: "test1"
  28. }
  29. },
  30. testCase1: {
  31. failed: 1,
  32. passed: 1,
  33. ignored: 0,
  34. total: 2,
  35. type: "testcase",
  36. name: "testCase1",
  37.  
  38. test0: {
  39. result: "pass",
  40. message: "Test passed.",
  41. type: "test",
  42. name: "test0"
  43. },
  44. test1: {
  45. result: "fail",
  46. message: "Assertion failed.",
  47. type: "test",
  48. name: "test1"
  49. }
  50. }
  51. }
  52.  
{
    failed: 2,
    passed: 2,
    ignored: 0,
    total: 4,
    type: "testsuite",
    name: "Test Suite 0",
 
    testCase0: {
        failed: 1,
        passed: 1,
        ignored: 0,
        total: 2,
        type: "testcase",
        name: "testCase0",
 
        test0: {
            result: "pass",
            message: "Test passed."
            type: "test",
            name: "test0"
        },
        test1: {
            result: "fail",
            message: "Assertion failed.",
            type: "test",
            name: "test1"
        }
    },
    testCase1: {
        failed: 1,
        passed: 1,
        ignored: 0,
        total: 2,
        type: "testcase",
        name: "testCase1",
 
        test0: {
            result: "pass",
            message: "Test passed.",
            type: "test",
            name: "test0"
        },
        test1: {
            result: "fail",
            message: "Assertion failed.",
            type: "test",
            name: "test1"
        }
    }
}
 

This example shows the results for a test suite with two test cases, but there may be test suites contained within test suites. In that case, the hierarchy is built out accordingly, for example:

  1. {
  2. failed: 3,
  3. passed: 3,
  4. ignored: 0,
  5. total: 6,
  6. type: "testsuite",
  7. name: "Test Suite 0",
  8.  
  9. testCase0: {
  10. failed: 1,
  11. passed: 1,
  12. ignored: 0,
  13. total: 2,
  14. type: "testcase",
  15. name: "testCase0",
  16.  
  17. test0: {
  18. result: "pass",
  19. message: "Test passed.",
  20. type: "test",
  21. name: "test0"
  22. },
  23. test1: {
  24. result: "fail",
  25. message: "Assertion failed.",
  26. type: "test",
  27. name: "test1"
  28. }
  29. },
  30.  
  31. testCase1: {
  32. failed: 1,
  33. passed: 1,
  34. ignored: 0,
  35. total: 2,
  36. type: "testcase",
  37. name: "testCase1",
  38.  
  39. test0: {
  40. result: "pass",
  41. message: "Test passed.",
  42. type: "test",
  43. name: "test0"
  44. },
  45. test1: {
  46. result: "fail",
  47. message: "Assertion failed.",
  48. type: "test",
  49. name: "test1"
  50. }
  51. },
  52.  
  53. testSuite0:{
  54. failed: 1,
  55. passed: 1,
  56. ignored: 0,
  57. total: 2,
  58. type: "testsuite",
  59. name: "testSuite0",
  60.  
  61. testCase2: {
  62. failed: 1,
  63. passed: 1,
  64. ignored: 0,
  65. total: 2,
  66. type: "testcase",
  67. name: "testCase2",
  68.  
  69. test0: {
  70. result: "pass",
  71. message: "Test passed.",
  72. type: "test",
  73. name: "test0"
  74. },
  75.  
  76. test1: {
  77. result: "fail",
  78. message: "Assertion failed.",
  79. type: "test",
  80. name: "test1"
  81. }
  82. }
  83. }
  84. }
  85.  
{
    failed: 3,
    passed: 3,
    ignored: 0,
    total: 6,
    type: "testsuite",
    name: "Test Suite 0",
 
    testCase0: {
        failed: 1,
        passed: 1,
        ignored: 0,
        total: 2,
        type: "testcase",
        name: "testCase0",
 
        test0: {
            result: "pass",
            message: "Test passed.",
            type: "test",
            name: "test0"
        },
        test1: {
            result: "fail",
            message: "Assertion failed.",
            type: "test",
            name: "test1"
        }
    },
 
    testCase1: {
        failed: 1,
        passed: 1,
        ignored: 0,
        total: 2,
        type: "testcase",
        name: "testCase1",
 
        test0: {
            result: "pass",
            message: "Test passed.",
            type: "test",
            name: "test0"
        },
        test1: {
            result: "fail",
            message: "Assertion failed.",
            type: "test",
            name: "test1"
        }
    },
 
    testSuite0:{
        failed: 1,
        passed: 1,
        ignored: 0,
        total: 2,
        type: "testsuite",
        name: "testSuite0",
 
        testCase2: {
            failed: 1,
            passed: 1,
            ignored: 0,
            total: 2,
            type: "testcase",
            name: "testCase2",
 
            test0: {
                result: "pass",
                message: "Test passed.",
                type: "test",
                name: "test0"
            },
 
            test1: {
                result: "fail",
                message: "Assertion failed.",
                type: "test",
                name: "test1"
            }
        }
    }
}
 

In this code, the test suite contained another test suite named "testSuite0", which is included in the results along with its test cases. At each level, the results are aggregated so that you can tell how many tests passed or failed within each test case or test suite.

TestRunner-Level Events

There are two events that occur at the TestRunner level:

  • TestRunner.BEGIN_EVENT - occurs when testing is about to begin but before any tests are run.
  • TestRunner.COMPLETE_EVENT - occurs when all tests in all test cases and test suites have been executed or ignored.

The data object for these events contain a type property, indicating the type of event that occurred. COMPLETE_EVENT also includes a results property that is formatted the same as the data returned from TEST_SUITE_COMPLETE_EVENT and contains rollup information for all test cases and tests suites that were added to the TestRunner.

Subscribing to Events

You can subscribe to particular events by calling the subscribe() method (see the documentation). Your event handler code should expect a single object to be passed in as an argument. This object provides information about the event that just occured. Minimally, the object has a type property that tells you which type of event occurred. Example:

  1. function handleTestFail(data){
  2. alert("Test named '" + data.testName + "' failed with message: '" + data.error.message + "'.");
  3. }
  4.  
  5. var TestRunner = YAHOO.tool.TestRunner;
  6. TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleTestFail);
  7. TestRunner.run();
  8.  
    function handleTestFail(data){
        alert("Test named '" + data.testName + "' failed with message: '" + data.error.message + "'.");
    }
 
    var TestRunner = YAHOO.tool.TestRunner;
    TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleTestFail);
    TestRunner.run();
 

In this code, the handleTestFail() function is assigned as an event handler for TEST_FAIL_EVENT. You can also use a single event handler to subscribe to any number of events, using the event data object's type property to determine what to do:

  1. function handleTestResult(data){
  2. var TestRunner = YAHOO.tool.TestRunner;
  3.  
  4. switch(data.type) {
  5. case TestRunner.TEST_FAIL_EVENT:
  6. alert("Test named '" + data.testName + "' failed with message: '" + data.error.message + "'.");
  7. break;
  8. case TestRunner.TEST_PASS_EVENT:
  9. alert("Test named '" + data.testName + "' passed.");
  10. break;
  11. case TestRunner.TEST_IGNORE_EVENT:
  12. alert("Test named '" + data.testName + "' was ignored.");
  13. break;
  14. }
  15.  
  16. }
  17.  
  18. var TestRunner = YAHOO.tool.TestRunner;
  19. TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleTestResult);
  20. TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleTestResult);
  21. TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleTestResult);
  22. TestRunner.run();
  23.  
function handleTestResult(data){
    var TestRunner = YAHOO.tool.TestRunner;
 
    switch(data.type) {
        case TestRunner.TEST_FAIL_EVENT:
            alert("Test named '" + data.testName + "' failed with message: '" + data.error.message + "'.");
            break;
        case TestRunner.TEST_PASS_EVENT:
            alert("Test named '" + data.testName + "' passed.");
            break;
        case TestRunner.TEST_IGNORE_EVENT:
            alert("Test named '" + data.testName + "' was ignored.");
            break;
    }
 
}
 
var TestRunner = YAHOO.tool.TestRunner;
TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleTestResult);
TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleTestResult);
TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleTestResult);
TestRunner.run();
 

Viewing Results

To view the results of tests, create a new YAHOO.tool.TestLogger object. The TestLogger is a subclass of the Logger widget that is specifically designed to output results from the TestRunner. Before calling TestRunner.run(), create a new instance of the TestLogger to ensure that all results are reported:

  1. var oLogger = new YAHOO.tool.TestLogger();
  2. YAHOO.tool.TestRunner.run();
  3.  
var oLogger = new YAHOO.tool.TestLogger();
YAHOO.tool.TestRunner.run();
 

Once created, you can filter the data to show only tests that passed, only tests that failed, etc.

Test Reporting

When all tests have been completed and the results object has been returned, you can post those results to a server using a YAHOO.tool.TestReporter object. A TestReporter object creates a form that is POSTed to a specific URL with the following fields:

  • results - the serialized results object.
  • useragent - the user-agent string of the browser.
  • timestamp - the date and time that the report was sent.

You can create a new TestReporter object by passing in the URL to report to. The results object can then be passed into the report() method to submit the results:

  1. var oReporter = new YAHOO.tool.TestReporter("http://www.yourserver.com/path/to/target");
  2. oReporter.report(results);
  3.  
var oReporter = new YAHOO.tool.TestReporter("http://www.yourserver.com/path/to/target");
oReporter.report(results);
 

The form submission happens behind-the-scenes and will not cause your page to navigate away. This operation is one direction; the reporter does not get any content back from the server.

Serialization Formats

There are two predefined serialization formats for results objects: XML (the default) and JSON. The format can be specified in the TestReporter constructor by passing in YAHOO.tool.TestFormat.XML or YAHOO.tool.TestFormat.JSON (when no argument is specified, YAHOO.tool.TestFormat.XML is used:

  1. var oReporter = new YAHOO.tool.TestReporter("http://www.yourserver.com/path/to/target", YAHOO.tool.TestFormat.JSON);
  2.  
var oReporter = new YAHOO.tool.TestReporter("http://www.yourserver.com/path/to/target", YAHOO.tool.TestFormat.JSON);
 

The XML format outputs results in the following format:

  1. <report name="YUI Test Results" passed="5" failed="3" ignored="1" total="5">
  2. <testsuite name="yuisuite" passed="5" failed="0" ignored="0" total="5">
  3. <testcase name="YAHOO.util.Anim" passed="5" failed="0" ignored="0" total="5">
  4. <test name="test_getEl" result="pass" message="Test passed" />
  5. <test name="test_isAnimated" result="pass" message="Test passed" />
  6. <test name="test_stop" result="pass" message="Test passed" />
  7. <test name="test_onStart" result="pass" message="Test passed" />
  8. <test name="test_endValue" result="pass" message="Test passed" />
  9. </testcase>
  10. </testsuite>
  11. </report>
  12.  
<report name="YUI Test Results" passed="5" failed="3" ignored="1" total="5">
    <testsuite name="yuisuite" passed="5" failed="0" ignored="0" total="5">
        <testcase name="YAHOO.util.Anim" passed="5" failed="0" ignored="0" total="5">
            <test name="test_getEl" result="pass" message="Test passed" />
            <test name="test_isAnimated" result="pass" message="Test passed" />
            <test name="test_stop" result="pass" message="Test passed" />
            <test name="test_onStart" result="pass" message="Test passed" />
            <test name="test_endValue" result="pass" message="Test passed" />
        </testcase>
    </testsuite>
</report>
 

The JSON format requires the JSON utility to be loaded on the page and outputs results in a format that follows the object/array hierarchy of the results object, such as:

  1. {
  2. "passed": 5,
  3. "failed": 0,
  4. "ignored": 0,
  5. "total": 0,
  6. "type": "report",
  7. "name": "YUI Test Results",
  8.  
  9. "yuisuite":{
  10. "passed": 5,
  11. "failed": 0,
  12. "ignored": 0,
  13. "total": 0,
  14. "type": "testsuite",
  15. "name": "yuisuite",
  16.  
  17. "YAHOO.util.Anim":{
  18. "passed": 5,
  19. "failed": 0,
  20. "ignored": 0,
  21. "total": 0,
  22. "type":"testcase",
  23. "name":"YAHOO.util.Anim",
  24.  
  25. "test_getEl":{
  26. "result":"pass",
  27. "message":"Test passed.",
  28. "type":"test",
  29. "name":"test_getEl"
  30. },
  31. "test_isAnimated":{
  32. "result":"pass",
  33. "message":"Test passed.",
  34. "type":"test",
  35. "name":"test_isAnimated"
  36. },
  37. "test_stop":{
  38. "result":"pass",
  39. "message":"Test passed.",
  40. "type":"test",
  41. "name":"test_stop"
  42. },
  43. "test_onStart":{
  44. "result":"pass",
  45. "message":"Test passed.",
  46. "type":"test",
  47. "name":"test_onStart"
  48. },
  49. "test_endValue":{
  50. "result":"pass",
  51. "message":"Test passed.",
  52. "type":"test",
  53. "name":"test_endValue"
  54. }
  55. }
  56. }
  57. }
  58.  
{
    "passed": 5,
    "failed": 0,
    "ignored": 0,
    "total": 0,
    "type": "report",
    "name": "YUI Test Results",
 
    "yuisuite":{
        "passed": 5,
        "failed": 0,
        "ignored": 0,
        "total": 0,
        "type": "testsuite",
        "name": "yuisuite",
 
        "YAHOO.util.Anim":{
            "passed": 5,
            "failed": 0,
            "ignored": 0,
            "total": 0,
            "type":"testcase",
            "name":"YAHOO.util.Anim",
 
            "test_getEl":{
                "result":"pass",
                "message":"Test passed.",
                "type":"test",
                "name":"test_getEl"
            },
            "test_isAnimated":{
                "result":"pass",
                "message":"Test passed.",
                "type":"test",
                "name":"test_isAnimated"
            },
            "test_stop":{
                "result":"pass",
                "message":"Test passed.",
                "type":"test",
                "name":"test_stop"
            },
            "test_onStart":{
                "result":"pass",
                "message":"Test passed.",
                "type":"test",
                "name":"test_onStart"
            },
            "test_endValue":{
                "result":"pass",
                "message":"Test passed.",
                "type":"test",
                "name":"test_endValue"
            }
        }
    }
}
 

Each of the formats produces a string with no extra white space (white space and indentation shown here is for readability purposes only).

Custom Fields

You can optionally specify additional fields to be sent with the results report by using the addField() method. This method accepts two arguments: a name and a value. Any field added using addField() is POSTed along with the default fields back to the server:

  1. oReporter.addField("color", "blue");
  2. oReporter.addField("message", "Hello world!");
  3.  
oReporter.addField("color", "blue");
oReporter.addField("message", "Hello world!");
 

Note that if you specify a field name that is the same as a default field, the custom field is ignored in favor of the default field.

YUI on Mobile: Using Testing Framework with "A-Grade" Mobile Browsers

About this Section: YUI generally works well with mobile browsers that are based on A-Grade browser foundations. For example, Nokia's N-series phones, including the N95, use a browser based on Webkit — the same foundation shared by Apple's Safari browser, which is found on the iPhone. The fundamental challenges in developing for this emerging class of full, A-Grade-derived browsers on handheld devices are:

  • Screen size: You have a much smaller canvas;
  • Input devices: Mobile devices generally do not have mouse input, and therefore are missing some or all mouse events (like mouseover);
  • Processor power: Mobile devices have slower processors that can more easily be saturated by JavaScript and DOM interactions — and processor usage affects things like battery life in ways that don't have analogues in desktop browsers;
  • Latency: Most mobile devices have a much higher latency on the network than do terrestrially networked PCs; this can make pages with many script, css or other types of external files load much more slowly.

There are other considerations, many of them device/browser specific (for example, current versions of the iPhone's Safari browser do not support Flash). The goal of these sections on YUI User's Guides is to provide you some preliminary insights about how specific components perform on this emerging class of mobile devices. Although we have not done exhaustive testing, and although these browsers are revving quickly and present a moving target, our goal is to provide some early, provisional advice to help you get started as you contemplate how your YUI-based application will render in the mobile world.

More Information:

YUI Test works with only minor issues on mobile devices. Things to be aware of:

  • Asynchronous tests may not work. Certain mobile browsers, such as Opera Mini 4, do not support the setTimeout() function, which is necessary for asynchronous tests. YUI Test will automatically revert to synchronous mode for running tests in this situation and any tests that the wait() method will throw an error.
  • Not all mouse and keyboard events are support on mobile devices, so attempting to simulate these events will cause tests to fail. Most mobile browsers do not support mouseover or mouseout and many do not support key events at all.

Support & Community

The YUI Library and related topics are discussed on the on the YUILibrary.com forums.

Also be sure to check out YUIBlog for updates and articles about the YUI Library written by the library's developers.

Filing Bugs & Feature Requests

The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

Copyright © 2013 Yahoo! Inc. All rights reserved.

Privacy Policy - Copyright Policy - Job Openings