Skip to content

A JavaScript white-box testing library written without any external dependencies

Notifications You must be signed in to change notification settings

oatkiller/testingjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Description

Testing.js is a very simple testing framework that has no external dependencies. It is very lightweight. It can run in browser or non-browser JavaScript environments. Use it to test your JavaScript code.

Structure

Testing.js has several classes:

  1. Runner: Logs test failures and successes with its registered listeners
  2. Suite: Organizes related tests, runs setUp and tearDown functions before and after each test run, and reports to a Runner.
  3. Test: Consists of a message and a test. Calls Asserts in order to test your code.
  4. Assert: Throws failure errors when code doesn’t work as expected. These errors are caught and passed to the Runner, which logs them.

Usage Example

// Testing.js is stored at a fully qualified location in the dom
// Run this function like this to pollute the global namespace with Testing.js functions.
// See Details for instructions on using the library namespaced
this['git:/oatkiller/testingjs.git']();
// A Runner is required.
// Runners create a default listener that reports to console.log, to override this, pass false when creating your Runner
var runner = new Runner();
// A Suite contains multiple tests
// A Suite config must specify a runner to report to
// A Suite config may specify any number of tests. The assertion should be in the property name, and the test is the function, like this:
var suite = new Suite({
	runner : runner,
	'1+1 should not equal 3' : function () {
		// Use the Assert function, pass it true when a test has passed, or false otherwise. The second param is a message to send when a failure happens
		Assert(1+1!==3,'they did equal 3');
	},
	// This should fail
	'1+1 should equal 3' : function () {
		// 'they didnt equal 3' will be reported as the reason this test has failed
		Assert(1+1===3,'they didnt equal 3');
	}
});
// Call run() on a suite to run all of its tests
suite.run();

Using the Optional setUp and tearDown methods

If passed to a Suite, the optional setUp method will run before each test is run. If the optional tearDown method is passed, it will be run after each test is run. The setUp method, tearDown method, and each test, will be run in the scope of their Suite. Since setUp, tearDown and your tests are all run in the same scope, they can assign properties to the Suite in order to pass a payload between each other.

Example

// In this example, I'll test the methods of a class
// A Widget class that has children
var Widget = function () {
	this.children = [];
};
// A hasChildren method that will return true if the widget has children
Widget.prototype.hasChildren = function () {
	return this.children.length > 0;
};
this['git:/oatkiller/testingjs.git']();
var runner = new Runner();
var suite = new Suite({
	runner : runner,
	// Define the optional setUp method
	// This will be run before each test
	setUp : function () {
		// We will use a new widget for each test
		this.widget = new Widget();
	},
	// Define the optional tearDown method
	// This will be run after each test
	tearDown : function () {
		// Destroy the widget that we just used
		delete this.widget;
	},
	'hasChildren should return true if a widget has 1 child' : function () {
		// Add a child
		this.widget.children.push({});
		// Test the method
		Assert(this.widget.hasChildren() === true,'hasChildren did not return true when widget had 1 child');
	},
	'hasChildren should return false if a widget has 0 children' : function () {
		// Test the method
		Assert(this.widget.hasChildren() === false,'hasChildren returned true when widget had 0 children');
	}
});
suite.run();

Using Export to Keep Testing.js Namespaced

In order to prevent identifier collisons, Testing.js is stored in a fully qualified location. If you’d like to pollute your global namespace with Testing.js, just run the setup function without passing any parameters. If you’d rather namespace Testing.js, just pass the setup function a namespace object.

Example

// Create an object to be a namespace
var testing = {};
// Pass that object when you call the setup function
this['git:/oatkiller/testingjs.git'](testing);
// Now the classes in Testing are stored on your namespace object
// Access the Runner class at testing.Runner
var runner = new testing.Runner();
// Access the Suite class at testing.Suite
// If the namespace object was stored at ns, you'd use ns.Suite
var suite = new testing.Suite({
	runner : runner,
	'1+1 should not equal 3' : function () {
		// Assert is also now stored on the namespace
		testing.Assert(1+1!==3,'they did equal 3');
	},
	// This should fail
	'1+1 should equal 3' : function () {
		testing.Assert(1+1===3,'they didnt equal 3');
	}
});
suite.run();

API Documentation

The setup function is located on the global object (window, if run in a web browser). The property it is stored under is : ‘git:/oatkiller/testingjs.git’.

this[‘git:/oatkiller/testingjs.git’]

Params

Param Type Optional? Desc.
exportObj Object Optional If passed, export the public classes into exportObj. If not passed, public classes will be exported into the global

Runner

The Runner class has listeners. Suite’s keep a reference to a runner. A runner instance is required to use testing.js

Constructor

Param Type Optional? Desc.
addDefaultListener Boolean Optional If false, the default listener that writes to console.log will not be added. Otherwise, the default listener will be added

Properties

Property Type Desc.
listeners Array Holds a reference to each listener added to a Runner instance
successCount Number The number of tests that have been run with success
failureCount Number The number of tests that have been run that failed

Methods

addListener

Param Type Optional? Desc.
handler Function Required The handler that will be called when a test is run, or when a report is run.
scope Object Optional The scope to run handler in

addListener registers a handler to a Runner. When the Runner logs a success, failure, or a report, the handler will be called with the type of message, and the payload.

Usage Example
this.addListener(function (messageType,payload) {
	// messageType will be 'report', 'success', or 'failure'
	if (messageType !== 'report') {
		// Successes and failures will have a assertionText and testFn property.
		// The assertionText property is the Assert text that the code failed
		// Failures will have an error property that is the Error that the Assert threw
		console.log(messageType,':',payload.assertionText,payload.error ? payload.error : '');
	} else {
		console.log(messageType,': success: ',payload.successCount,' failure: ',payload.failureCount);
	}
})
handler
The handler passed to addListener should follow this signature
Param Type Optional? Desc.
messageType String Required The type of message being handled. ‘success’ if a test has passed without failing. ‘failure’ if a test failed when run. Or ‘report’ when the Runner is running a report on how many tests failed vs. passed
payload Object Required An object with properties about the message being handled
payload
The handler accepts a payload with different properties depending on which type of message.

success

testFn Test fn that ran successfully
assertionText The assertion the test makes

failure

testFn Test fn that ran successfully
assertionText The assertion the test makes
error The error that the failing Assert threw

report

successCount The number of successful tests
failureCount The number of failed tests

log

private

This method fires listeners with a message. This is called when a Suite runs tests

report

private

This method fires listeners with a message about how many tests have passed or failed

Assert

The Assert function throws an Error if passed false for its first param. Use Assert in tests to check that your code works

Param Type Optional? Desc.
passed Boolean Required True to pass, false to fail
message String Optional The message to log that explains why this code failed the test

Usage Example

// This should fail
'1+1 should equal 3' : function () {
	testing.Assert(1+1===3,'they didnt equal 3');
}

Equals

The Equals function throws an Error if its first two parameters evaluate to false when compared with ====’. The error message will include information about the actual value and the expected value. An additional error message is optional.

Param Type Optional? Desc.
actual Anything Required A value generated by a test
expected Anything Required The value you expect `actual` to point to
message String Optional An additional error message to include along with the automatically generated one

Usage Example

// This should fail
'1+1 should equal 3' : function () {
	testing.Equals(1+1,3,'I am not good at math.');
}
// Produces an error like: failure : 1+1 should equal 3 : "I am not good at math. : 2 was not 3 (expected)" 

Wait & Resume

The Wait and Resume functions are used to test asynchronous code. Call Wait with a timeout handler and a wait duration. When your asynchronous code completes, call Resume. If the wait duration expires before Resume is called, an error will occur and the timeout handler will be called.

Equals
|Param|Type|Optional?|Desc.|
|timeoutHandler|Function or Number|Required|A function to call if the wait duration expires. Optionally pass the retuned value from setTimeout|
|waitDuration|Number|Required|Milliseconds before timing out|

Resume
|Param|Type|Optional?|Desc.|
None

Usage Example

// Wait one second for an ajax call to complete before asserting its value
// Note: The ajax methods here are made-up.
var ajaxRequest = new Request('test.html');
Wait(function () {
	// one second passed without the ajax call completing. An error will occur. Clean up the ajax request by cancelling it.
	ajaxRequest.cancel();
},1000);
// start the ajaxRequest
ajaxRequest.happen({
	// the call completed
	success : function (response) {
		Equals(response.code,301);
		// the ajax request is done. Continue running the test suite.
		Resume();
	}
});

Test

The Test class can be created directly, or by passing a test config to your Suite

Constructor

Param Type Optional? Desc.
assertionText String Required The assertion that you are making with this test
testFn Function Required The test function

Properties

Property Type Optional? Desc.
assertionText String The assertion that you are making with this test
testFn Function The test function

Usage Example

Defining Tests with the Suites Config

You should probably define Tests with the Suites config

 var suite = new Suite({
	runner : runner,
	// any non-special property name is evaluated as an assertionText with the function is points to evaluated as a testFn
	// this creates a test just as if you'd called new Test('1+1 should not equal 3',function () { ...  });
	'1+1 should not equal 3' : function () {
		Assert(1+1!==3,'they did equal 3');
	}
});
*Defining Tests with the new keyword*
var test = new Test('1+1 evaluates to 2',function () {
	Assert(1+1 === 2,'1+1 did not evaluate to 2');
});

Methods

run

private

This method is called internally when a Suite runs its tests

Suite

The Suite class has Test instances, a Runner instance, and an optional setUp and optional tearDown method.

About

A JavaScript white-box testing library written without any external dependencies

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published