I am building upon the great example of unit testing in javascript by Stephen Walther, and will now present additional methods to the JavascriptUnitTestingFramework.js file he presented.
Let me first list up the new contents:
var assert = {
areEqual: function (expected, actual, message) {
if (expected !== actual) {
throw new Error("Expected value " + expected
+ " is not equal to " + actual + ". " + message);
}
},
areNotEqual: function (expected, actual, message) {
if (expected === actual) {
throw new Error("Expected value " + expected
+ " is equal to " + actual + ". " + message);
}
},
isTrue: function (actual, message) {
if (!actual) {
throw new Error("The provided actual value" + actual + " is not evaluating to true. " + message);
}
},
isNull: function (actual, message) {
if (actual != null) {
throw new Error("The provided actual value " + actual + " must be null. " + message);
}
},
isNotNull: function (actual, message) {
if (actual == null) {
throw new Error("The provided actual value " + actual + "must be not null." + message);
}
},
isFalse: function (actual, message) {
if (actual) {
throw new Error("The provided actual value " + actual + " must be false. " + message);
}
},
isInstanceOfType: function (actual, type, message) {
if ((actual instanceof type) == false) {
throw new Error("The provided actual value " + actual + " is not an instance of type " + type + ". " + message);
}
},
referenceEquals: function (actual, expected, message) {
if (actual !== expected) {
throw new Error("The provided actual and expected values does not point to the same object. Actual is " + actual + " and expected is " + expected + " " + message);
}
}
};
I have added much of the methods of the Assert class in C#. The Assert class have static methods such as ReferenceEquals, IsTrue, IsFalse, AreNotEqual and so on and these methods have now been added above.
Lets continue with the javascript unit tests using this js file.
//At the start of the file for your javascript unit tests
//add a reference to the unit testing reference file JavascriptUnitTestingFramework.js
//Use the reference element and three slashes.
function testAddNumbers() {
// Act
var result = addNumbers(1, 3);
// Assert
assert.areEqual(4, result, "addNumbers did not return right value!");
}
function testFactorial() {
var result = factorial(5);
assert.areEqual(120, result, "factorial did not return righ value!");
}
function testIsTrue() {
assert.isTrue(110 > 0, "This must evaluate to true for the test to pass.");
}
function testIsNull() {
var city = null;
assert.isNull(city, "Must be null");
}
function testIsNotNull() {
var city = { name: "London" };
assert.isNotNull(city, "Must be not null");
}
function testIsFalse() {
var actual = 4 > 5;
assert.isFalse(actual, "The expression must evaluate to false.");
}
function testIsInstanceOfType() {
assert.isInstanceOfType(new String("mystring"), String, "The provided actual value must be an instance of type String.");
assert.isInstanceOfType([1, 2, 3, 4], Array, "The provided value must be an
instance of an array.");
}
function testAreNotEqual() {
assert.areNotEqual("abc", "abbc", "Provided expected value and actual value are not equal");
}
function testAreEqual() {
assert.areEqual("def", "def", "The two provided values does not agree.");
}
function testReferenceEquals() {
var a = { city: "Manchester" };
// var c = { country: "Ireland" };
var b = a;
assert.referenceEquals(a, b, "The two objects does not point to the same object.");
}
The unit tests written in C# then looks like this:
[TestMethod]
public void TestFactorial()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testFactorial");
}
[TestMethod]
public void TestIsTrue()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testIsTrue");
}
[TestMethod]
public void TestIsNull()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testIsNull");
}
[TestMethod]
public void TestIsNotNull()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testIsNotNull");
}
[TestMethod]
public void TestAreEqual()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testAreEqual");
}
[TestMethod]
public void TestIsFalse()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testIsFalse");
}
[TestMethod]
public void TestIsInstanceOfType()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testIsInstanceOfType");
}
[TestMethod]
public void TestAreNotEqual()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testAreNotEqual");
}
[TestMethod]
public void TestReferenceEquals()
{
var jsHelper = new JavaScriptTestHelper(this.TestContext);
jsHelper.LoadFile("JavaScriptUnitTestFramework.js");
jsHelper.LoadFile(@"..\..\..\MvcApplication1\Scripts\Math.js");
jsHelper.LoadFile("MathTest.js");
jsHelper.ExecuteTest("testReferenceEquals");
}
Ok, so as the methods above show that much of the action should take place in the TestInitialize method. I will leave that to figure out for yourself, basically the methods will have some similar Arrange for the individual unit tests.
So what we now have is a basic way of performing unit tests against javascript using MSTest with methods such as AreNotEqual, AreEqual, IsNull, IsNotNull, ReferenceEquals, IsTrue, IsFalse that are available when doing ordinary C# unit tests.
Using Stephen Walter's solution makes the client code developer focus at the task at hand at writing Javascript code and companion unit test. I would not suggest that you unit test the jQuery based javascript "Gui code", since you need to have a document loaded (with companion DOM tree). Instead, what you should unit test of your javascript code would be your custom logic which is kind of independent of the "GUI" and as such is suitable to doing unit tests and TDD. I find myself now using these unit tests as a quick way of performing TDD with javascript. When developing the factorial function for example, I could verify that the code I was writing by remembering my university days of how a factorial function was indeed spot on by executing a unit test. These unit tests are alsovery convenient to modify and executes very quickly, outside any browser and web server.
I will now include a zipped file that contains a Visual Studio 2010 solution with a test project that shows how the unit tests are for javascript code is running under MSTest. You will need Microsoft Visual Studio 2010 with support for test projects to open up the solution, this is not supported in Visual Studio 2010 Express.. Many thanks to Stephen Walther for making the unit testing in javascript possible!
Download the zipped solution with the javscript unit testing framework and companion unit tests in C¤ / javscript here right here from 2Shared drop location:
Download zipped solution here of this article [zip]
Unit testing in Javascript done the right way!
The tests above are passing, but please evaluate the code before you use it in any production code. If you find additional methods that makes the sample more complete, please let me know. I believe this strategy is superior to the one of QUnit which needs manual inspection in the browser. What we want is automatic tests running under MsTest and can be integrated into MSBuild and even gated checkins in TFS. Finally the way of doing TDD in Javascript is at hand!
ReplyDelete