My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label test. Show all posts
Showing posts with label test. Show all posts

Saturday, February 18, 2012

JavaScript Test Framewors: more than 30 + 1

After @szafranek hint and suggestion, wru landed almost at the end of this Wikipedia List of unit testing frameworks page.

If you use this tweet size hand made imperfect script in the wikipedia page console:

a=q="querySelectorAll",[].map.call(document[q](".mw-headline,.wikitable"),function(v,i){i%2?a=v.textContent:o[a]=v[q]("tr").length},o={}),o

You gonna see that JavaScript is third when it comes to number of test frameworks ... and not sure that's good, anyway, here a quick description of mine.

About wru

You can find most info in github page itself but essentially wru is a generic purpose, environment agnostic, JavaScript tests framework compatible with both client and server, where client means every bloody browser, and server means Rhino, node.js, and recently phantom.js too.
To be really fair, wru is not exactly a unit test framework since it does not provide by default anything related to mocks or stubs.
However, wru is so tiny and unobtrusive that any 3rd parts library could be integrated without effort in whatever test you want + need.

wru In A Nutshell

Well, the first thing to do with wru is to write code such:

wru.test([
{
name: "the test name, for feedback purpose",
setup: function (obj) {
// the OPTIONAL setup property performed before the test
// obj will be a freshly new created object
// with a single test lifecycle ... reusable within the test
},
test: function (obj) {
// obj comes from setup, if any
wru.assert(true); // a generic truish condition
setTimeout(wru.async(function () {
// a generic asynchronous condition
// where you will inevitably assert something
wru.assert("this is also true", true);
}), 1000);

// the handy possibility to reuse assert itself
if (wru.assert("this is a truish value", obj)) {
// do stuff with obj
// or simply log the object
wru.log(obj);
}
},
teardown: function (obj) {
// the OPTIONAL teardown property performed after the test
// obj comes from the test function, is the same
// passed through setup, if present, and test
}
}
]);

After this you are basically ready to go.
Both assert() and async() accept one up to two arguments: the optional description of the test, recommended if you want at least understand what's going on and where if something failed, and the second parameter which is returned in both cases.
Via assert() the condition/expression is evaluated as it is, truish or falsy, and returned if addressed.
Via async() the callback is wrapped in an unobtrusive way and this is simply to let wru know that something is going to happen, hopefully in a reasonable timeout of 10 seconds.

How To See Results

Once you have your test in place, you can re-use the same code all over supported environments.
The cool part about wru is that templates for all scenarios are automagically created at build time.
You don't even need to npm install wru to use the test, or include it in a page via script tag, you can simply grab a template and copy and paste or replace, during a build process, the test. This will make your life easier than any setup oriented test framework, isn't it?

Write Once, Run Everywhere

Isn't this the dream we all have about JavaScript in both browsers and server side environments? As far as I know wru is the only one that supports all these environments with a unified, cross platform, and easy to remember API. (until I have discovered JS Class via @jcoglan)
Main principles behind wru? KISS and YAGNI, so that everyone can start, at least, writing tests for what's needed, without any ulterior waste of time about environment, library dependency, or programming language setup.
And what if you want to test platform specific gotchas ? Oh well, you can still recycle the whole test and check the number of positive results, as expected, at the end ... well, not all the code we are writing should work cross platform, but even in this case, wru gonna make you familiar with tests and tests logic so it's a win win in any case.

Pluggable UT Libraries

You must admit the name of this framework is absolutely crap. I didn't even know how to call it in a manner that no other frameworks would have been affected, so I sticked with my blog initials, WebReflection, and the one out of Unit in it, until I introduced this tiny library in one of those amazing BerlinJS events where someone suggested Where Are You acronym, and I simply loved it ... uh wait, let's go back in the topic ...
Any external library able to mock or stub your code should be easy to integrate in a wru test without problems.
In this case you may need to include this library upfront via script tag, in the Web html template, or inside any server side related template through require() or some other mechanism.
For browsers, you may consider JSMock, as example, but others which aim is to provide mocks or stubs functionality should be all supported without problems.

About Asynchronous Logic

Let's face reality, asynchronous tests are created exclusively to test the condition after the asynchronous callback has been executed, and this is exactly the wru expectation, you call for async? you wait for async.
If you get how async work you'll realize that you don't have to expect() anything, simply do what your test should do and trigger the done() at the end.
This comes from one of the most appreciated asynchronous pattern out there, Promises, where you simply wait for a done() to be called.
wru does the same, except the equivalent of done() is assert() which is the trigger.
If you have truly complex asynchronous logic, here a common pattern you might find useful with wru.

wru.test([{
name: "async",
test: function () {
var expectation = wru.async(function (arg) {
wru.assert("everything went better than expected", arg === "OK");
});
// your whatever async logic you need
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function () {
// called multiple times during xhr lifecycle
if (xhr.readyState === 4) {
// async, inside async
doSomeStuffOutThere(xhr.responseText, function (e) {
expectation(e.result); // hopefully the string "OK"
});
}
};
}
}]);

in few words, you don't need to expect a thing since once you define a single asynchronous callback in your test logic, you can trigger it once everything has been tested and, if that will never happen, the timeout will automatically flag the test as failed.

wru Output

Things are kept simple in this case as well, with the happy exception for the cursor.
I mean, you don't really need to stare at it, but the cursor is a visual indication that everything is not just stuck, but it's simply waiting for things to happen, if ever.
A classic infinite loop or endless generator is able to freeze the lifecycle of our app, and only a visual feedback will be able to tell you the truth since any other method, specially in browsers where tests are showed only once executed, won't be able to give you a thing ... except a crash in the best case scenario.
The cursor may interfere with the normal output but, at least when it comes to server side tests, whatever external tool will be able to remove cursor noise in the log and analyze anything that happened during test execution, from green to red or black, providing you an instant feedback if something went wrong.

Improve As You Go

wru is not a universal answer to JS tests but hopefully a good start point or most likely everything you need if the project, and your tests, are not that complex.
Being that simple, the best thing about this library is that we could reproduce its API everywhere else in few lines of code, transforming, as example, your tests into another UT framework without much effort.
The fragmentation of JS tests out there is massive, so I don't really want to go deeper into this, just think that this library made asynchronous tests that easy, as well as normal synchronous one and without interferences, thanks to the chosen namespace, out there.
What else about wru ... well, give i a try :)

Tuesday, November 08, 2011

Function.prototype.notifier

There are way too many ways to stub functions or methods, but at the end of the day all we want to know is always the same:
  • has that function been invoked ?
  • has that function received the expected context ?
  • which argument has been passed to that function ?
  • what was the output of the function ?

Update thanks to @bga_ hint about the output property in after notification, it made perfect sense

The Concept

For fun and no profit I have created a prototype which aim is to bring a DOM like interface to any sort of function or method in order to monitor its lifecycle:
  • the "before" event, able to preventDefault() and avoid the original function call at all
  • the "after" event, in order to understand if the function did those expected changes to the environment or to a generic input object, or simply to analyze the output of the previous call
  • the "error" event, in case we want to be notified if something went wrong during function execution
  • the "handlererror" event, just in case we are the cause of an error while we are monitoring the original function
The reason I have chosen an addEventListener like interface, called in this case addListener, is simple: JavaScript works pretty well with event driven applications so what else could be better than an event driven approach?

Basic Example


var nFromCharcode = String.fromCharCode.notifier({
before: function (e) {
if (e.arguments.length > 2048) {
throw "too many arguments";
e.preventDefault(); // won't even try to execute it
}
// in case you want to remove this listener ...
e.notifier.removeListener("before", e.handler);
},
after: function (e) {
if (e.output !== "PQR") {
throw "expected PQR got " + e.output + " instead";
}
},
handlererror: function (e) {
testFramework.failBecause("" + e.error);
}
});

// run the test ...
nFromCharcode(80, 81, 82); // "PQR"
nFromCharcode.apply(null, arrayOf2049Codes); // testFramework will fail

The notifier itself is a function, precisely the original function wrapper with enriched API in order to monitor almost every aspect of a method or a function.
The event object passed through each listener has these properties:
  • notifier: the object create to monitor the function and notify all listeners
  • handler: the current handler to make the notifier remove listener easier
  • callback: the original function that has been wrapped by the notifier
  • type: the event type such before, error, after, handlererror
  • arguments: passed arguments transformed already into array
  • context: the "this" reference used as callback context
  • error: the optional error object for events error and handlererror
  • preventDefault: the method able to avoid function execution if called in the before listener
  • output: assigned only during "after" notification and if no error occurred, handy to compare expected results

I guess there is really nothing else we could possibly know about a notifier, and its callback, lifecycle, what do you think?

The Code




As Summary


I have also a full test coverage for this notifier and I hope someone will use it and will come back to provide some feedback, cheers!

Wednesday, September 16, 2009

I Would Simply Like To Support iPhone But I Cannot

Update
Before other silly flame/blame will start, I have already twitted that my next laptop will be a netbook. Here the related tweet.


let me summarize the story: at that time I had a girlfriend with an iPhone so I though why on earth shouldn't I buy an Android instead? If I need some test online I'll have both to test it, and I've rarely been that wrong.

Wanna Develop for iPhone? Buy a Mac

As marketing strategy sounds "cool" except I do not want to develop iPhone application, the present and the future is online, why on earth should I learn another programming language from the scratch, Objective-C, to develop something that will be relatively soon replaceable via WebKit and a good host?

Wanna Develop Web for iPhone? Buy a Mac

Again!!! Unbelievable, there is not such a thing: online iPhone tester, everything I found Googling was about a silly image with an iframe just to test the iPhone size ... are you kidding me?
It's like put IE inside an iframe inside an "Android frame" ... where is webkit? Where is the real JS engine? Where is the real render engine?

Wanna Develop for Apple Devices? Buy Apple

OK, I got your point, so now I am stuck with an Android contract and moreover I "love" my Android and it's about a year I am using it writing at light speed thanks to hidden keyboard, and using for whatever Web purpose thanks to its WebKit which is maybe not perfect but it works like a charm, generally speaking.
I do not want to buy a Mac only to test iPhone Web engine via Xcode, I'd love to have better reasons for a Mac, and I don't want to buy an iPhone only to test it online.
That will mean another contract to surf the Net as well ... and why on earth should I do this while I am simply trying to support iPhone?

I Would Simply Like To Support iPhone But I Cannot

Now I am a single developer, but I cannot imagine all other engineers, JavaScript hackers, W3C "slaves", cross-browser maniacs, unable to develop a web application for iPhone. With this strategy Apple is missing the possibility to fix a wider range of problems related to iPhone WebKit dedicated build and moreover it is avoiding iPhone Web support for "silly developers" like me that in this case was only trying to fix a common problem, the css position fixed property, but I am unable to make it works in an iPhone while every other A-Grade browser is supported, Android included, and here is the example!

As Summary

People like Peter-Paul Koch have the privilege to try any kind of mobile device in order to test compatibility. Unfortunately for Mobile Devices Vendors Peter-Paul alone will never be able to cover every kind of test. Even Microsoft releases time bombed images for Virtual Machines in order to test other versions of Internet Explorer. The Android OS is Open Source and it is possible to test it almost via whatever platform, same is for other browsers but not the iPhone. I know you do not mind but you are missing my contribute and support for projects a la sessionStorage, Formaldehyde, the incoming position-fixed, vice-versa or noSWFUpload ... and again, I am only one out of million of web developers.

Please think about it,
Best Regards

Wednesday, February 27, 2008

[ES4] ... too horrible to be an editor ...

... but probably simple enough for these milestones.

Its name is ES4me, it's for windows and works in this way:

  • download the file in your es4 folder, where there is the run.exe and the es4.bat file

  • double click



Basically, there is a place to write something, where for some reason TABS are usable only with CTRL + TAB instead of single TAB key, a panel where errors or output will be showed, and finally, 5 buttons:

  • Run, to execute the code

  • Clear, to clear the code in the area

  • Add Before, to add area code before execution

  • Get Before, to add in area the code added before

  • Clear Before, to clear content added before



Here some example:

var i:int = 123;
print(i);

Press Run, and You'll read 123 on output panel.
Now press clear, and area will be without text.
Write again ...

var i:int = 123;

Without the print, now press Add Before, area will be clean again ... BUT, write this:

print(i);

and press Run, you'll read 123 in the output panel.
This mean that precedent code is in ram, and is present on output but not in the area.
Of course, if you Add Before print(i); , this will be executed after var i:int = 123;

This is useful to add classes or other scripts, just tested.
Now, if you press Get Before, the entire content will be added to the area, after current one, if any.

Finally, with Clear Before, everything in the Ram will be cleaned.

Simple? Last thing: every time you press Run, code is compiled in a new enviroment.

That's it, and sorry for this horrible debugger ... but time is never enough :)