I was creating a small banner rotation script yesterday.  It needed to be tiny, lightweight and easy to deploy, so I chose to do it in JScript as it is available on every Windows machine.  I usually do small automation projects in Ruby, but the machine that has to run the script does not have Ruby installed.  I am a closure freak, so I went for JScript (VBScript does not have closures).


The script has to select a random subfolder from the source location and copy all its files to the destination.  I don't have much experience with Windows Scripting Host's (WSH) FileSystemObject and its File and Folder minions, so I decided it would be fun to do some learning tests and test-drive the entire implementation.  WSH allows you to create XML-based .wsf files that allow you to mix scripts in several languages and reference other script files.  This is all I needed to create a minimal test framework and a test runner.  My run-tests.wsf file looked initially like this:

<job>
    <script type="JScript" src="test-lib.js" />
    <script type="JScript" src="rotator.js" />
    <script type="JScript">
        //...
    </script>
</job>

Note that it references test-lib.js and rotator.js.  These are the test framework and the actual implementation.  My tests go inside the inline <script> block.  So I am ready for my first test.  I hardcoded some assumptions about the filesystem before the action and added assertions on how I thought it would look afterwards.  Here it is:

test(function(){
    var folder = new Folder("data1");
    assertEquals("2 files", 2, folder.Files.Count);
   
    if (fso.FileExists("index.html"))
        fso.DeleteFile("index.html");

    assert("no file before copy", !fso.FileExists("index.html"));
    folder.copyTo(".");
    assert("file copied successfully", fso.FileExists("index.html"));
   
});


Folder is my wrapper around the stock Folder and File objects.  The assert() and assertEquals() functions are all we need to verify if our program behaves according to our expectations.  A test is just a closure, containing assertions.  All of them are defined in test-lib.js  Here is how assertEquals() looks like:

function assertEquals(message, expected, actual)
{
    if (expected != actual)
    {
        throw "Expected: <" + expected.toString() + ">, actual <" + actual.toString() + ">.\n" + message;
    }
}

test() gets a single parameter that it executes in a try-catch block, which takes care of error and failure reports:

function test(body)
{
    try
    {
        body();
    }
    catch(error)
    {
        WScript.Echo("error: " + error.toString() + "\n" + error.message);
    }
}


I did play around with WSH's filesystem API until I got to know it better.  I experimented with stuff, deleted it or moved into the main implementation.  I went in tiny steps and that saved me a lot of time when debugging cryptic "This object does not support this property or method" error messages.  I did not know how to generate random numbers in JScript, but I learned that quickly with Google's help.  I am particularly proud of my randomization test:

test(function(){
    var folders = new Folders(".");
    assertEquals("exactly two subfolders", 2, folders.count());

    var random = [];
    for (var i = 0; i < 20; i++)
    {
        random.push(folders.randomSubFolder());
    }

    assert("has data1", hasSubFolder("data1", random));
    assert("has data2", hasSubFolder("data2", random));
});






Yes, I know that in theory it could fail without the program being wrong.  It could randomly select the "data1" subfolder 20 times in a row, but it has never happened.  If that happens I will crank the number up to 200.

I am done with the program.  My code is stored in rotator.js.  I configure some variables with the appropriate paths in rotator.wsf, and call the implementation I have test-driven so far:

<job>
 <script type="JScript" src="rotator.js" />
 <script type="JScript">
        //don't forget to escape backslashes in paths e.g.
        //C:\\somefolder\\somesubfolder
        var sourceFolder = ".";
        var destinationFolder = ".";
       
        copyRandom(sourceFolder, destinationFolder);
    </script>
</job>


I learned a lot from that experiment.  I am really comfortable with the WSH filesystem API now.  I liked the way I could hit Undo when I saw an error that I could not fix in seconds.  I tried different approaches to the problem -- some of them worked well, others had to go.

Notice how easy it is to grow your own test framework?  I did not need fancy features like setUp and tearDown.  I got away without a GUI -- the console told me everything I needed to know.  Ron Jeffries' advice from Extreme Programming Adventures in C# proved quite true: "You don't need a framework.  You need tests."

My testing "framework" was inspired by Phlip's NanoCppUnit defined in his recursive descent parser TDD example.

	
Hristo Deshev is a Principal Software Engineer at Telerik
About the Author

Hristo Deshev

Hristo Deshev is a Principal Software Engineer at Telerik.

Comments

Comments are disabled in preview mode.