Showing posts with label ant. Show all posts
Showing posts with label ant. Show all posts

2012-02-06

Unit testing Google Closure applications from the command line

I've been playing around with building an application using Google Closure. I tried searching for a good way to run the unit tests from the command line as part of an Ant build, but either there wasn't anything out there to do what I wanted to do or I just couldn't find it.
As an aside, naming a language after a programming construct is really dumb. If you try searching for blog posts on Google Closure, you get a bunch of stuff about javascript and closures since they're such an important language concept. I think Prototype might have been a more successful language if they hadn't named themselves after a language construct as well, though the framework itself has its issues.
I'm used to writing code using test driven development, which doesn't work particularly well writing some Javascript. DOM-related code in particular is difficult to write with TDD. But there are parts of Javascript applications that can and should be well unit tested. When coding in PHP or Python, I normally have two terminal windows open for my Vim sessions and one for my build and source code management activities. I'll typically save the file I'm working on and them immediately alt-tab to the build window and hit up then enter to run my unit test target. I really wanted to do that with Javascript.

At my job we use jQuery. This comes with Qunit which is easy to run automagically with PhantomJS. But Qunit didn't look like it would be easy to make work with Closure in that it wouldn't handle the dependencies for me. And I might as well use the unit test framework for the library that I'm working with. I had a few goals. I didn't want my Javascript tests to fire up a browser. That kind of thing should be handled by Selenium. I wanted to be able to add new tests without having to manually add the test name to any file. I wanted the output to be at least somewhat pretty and clean, particularly if there were no failures. As a PHP developer I'm used to the PHPUnit output:


I decided to try to code up my own test runner to make this work. Here's the first version:


First, the build target concatenates all of the test files. In my case, test files are in a directory called tests and each of them ends with Test.js.

Next, it fires up the Closure Compiler. In this example, we've got a Javascript file called foo.js. We compile that with the concatenated test file (tests-concat.js) and the test runner (we'll get to that soon). The compiler creates a file called tests.js. We run that with phantomjs. The build will look something like this:


Most of the magic happens in testRunner.js:

Basically how it works is to override some methods in goog.testing.TestCase to capture the results. We don't particularly care about successes, so they're replaced by dots. Failures show as an F and any errors are explained in more detail after all tests finish.

I've uploaded all of the example code to Github at https://github.com/omnicolor/Closure-CLI-test-runner.

2010-11-30

Using YUI compressor in an ant build process

Style sheets can get really large, really quickly. To make things easier to find, I split my CSS files out into major pieces of functionality. Instead of having to search through one giant style sheet of doom to find the rule that changes how a form field appears, I can open the style sheet for that bit of functionality. Many of my forms are customized, so each might have its own style sheet.

So I may have a bunch of CSS files in my styles directory:
  • general.css
  • login.css
  • signup.css
  • profile.css
Each rule in each of those files is namespaced by adding an ID attribute to the page's body tag, a class attribute to the body tag, or both. Then to deploy, the files need to be joined. I used to just concatenate the files together, but the YUI compressor does a great job making the files smaller that I started using that as well.

Every page on my site requires /styles/c.css. To generate that file from the components, I use some ant rules.

First, I make sure that I've download the YUI compressor. Since I have more than one project that uses this same system, I download the YUI compressor to a common location. And since I'm too lazy to worry about setting up my build environment on any system that I use I've included the rules to actually download the library. This target will only download the library once. If you override the yuicompressor property you can move the jar to wherever you want. I stick mine in tmp.

<property name="yuicompressor"
    value="/tmp/yuicompressor.jar" />
<available file="${yuicompressor}"
    property="yuicompressor.exists" />
<target name="yui-compressor-download"
        unless="yuicompressor.exists"
        description="Download and unpack YUI compressor">
    <echo message="Downloading YUI compressor" />
    <exec executable="wget">
        <arg value="--no-clobber" />
        <arg value="--no-verbose" />
        <arg value="--output-document=/tmp/yuicompressor.zip" />
        <arg value="http://yui.zenfs.com/releases/yuicompressor/yuicompressor-2.4.6.zip" />
    </exec>
    <unzip src="/tmp/yuicompressor.zip" dest="/tmp">
        <patternset>
            <include name="**/build/yuicompressor-2.4.6.jar" />
        </patternset>
    </unzip>
    <move file="/tmp/yuicompressor-2.4.6/build/yuicompressor-2.4.6.jar"
          tofile="${yuicompressor}" overwrite="true" />
    <delete file="/tmp/yuicompressor.zip" />
    <delete dir="/tmp/yuicompressor-2.4.6" />
</target>

Then, to actually build the combined and shrunk CSS file, I use a rule like this:

<uptodate property="compile-css.notRequired" targetfile="c.css">
    <srcfiles dir="styles" includes="**/*css" />
</uptodate>
<target name="compile-css" depends="yui-compressor-download"
        unless="compile-css.notRequired">
    <echo message="Compressing CSS" />
    <concat append="no" destfile="style-combined.css" force="no">
        <file file="styles/general.css" />
        <file file="styles/login.css" />
        <file file="styles/signup.css" />
        <file file="styles/profile.css" />
    </concat>
    <java jar="${yuicompressor}" fork="true">
        <arg value="style-combined.css" />
        <arg value="--charset=UTF-8" />
        <arg value="--verbose" />
        <arg value="-o" />
        <arg value="c.css" />
    </java>
    <delete file="style-combined.css" />
</target>
If the YUI compressor has not been downloaded, it will call the download rule for it. Then it concatenates all of the styles together and compresses them. The uptodate rule makes sure that it only runs if one of the files has changes.
Update: Changed the YUI version number to the newest version and re-added the code that actually builds the CSS. Not sure where it went.