Continuous Integration Is a Hack!
“Continuous Integration is a hack!” said my friend Ben Rady years ago during a discussion on CI hosted by Stelligent. At the time, I was incredulous! How dare someone question the value of CI, especially when we had just finished writing a book about it! What’s more, our book had been nominated for a prestigious Jolt award; indeed, the following day, our CI book won it!
In retrospect, Ben’s point was poignant: CI is reactionary. You still have to wait some amount of time to ascertain correctness. That is, CI implicitly relies on a passive process to run a project’s build and any corresponding tests. If those tests fail, you are of course, notified. Nevertheless, that notification is somewhat delayed: by the time a CI process runs the tests and reports on their status, you’ve already moved on to the next task. So much for failing fast!
On the other hand, if tests, which are arguably the quintessential proof of valid integration, are run continuously, then there’s no wait time to ascertain issues! Ben’s stance is really no surprise (especially if you know him) as he went on to play a major role in Infinitest and co-author Continuous Testing: with Ruby, Rails, and JavaScript. I’ve come to realize Ben’s wisdom since then and have become a huge fan of Continuous Testing.
A more proactive means to ensuring all is kosher with a code base is to continuously run your tests as you work. In fact, an even more superior process would be to run any tests for code that has changed. That is, once a mapping has been established between code under test and a corresponding test, then when that code changes, the quickest, arguably most effective way to ensure that code isn’t broken is to run its test(s).
One efficient way to ascertain mapping is to name tests after the code they verify proceeded with a test
moniker. For example, an Account
object would have its corresponding test called AccountTest
(or AccountSpec
, etc). Any time the Account
object changes, then the AccountTest
would be run.
Continuous Testing is an established practice in the world of Ruby. A great framework that facilitates it is called Guard. Briefly, Guard is a framework for watching a file system and sending out a notification upon some event (like a change). With Guard, you can define specific file matching patterns and a corresponding action to take when an event is triggered. You can probably see that this linkage facilitates running tests continuously.
There’s really no corollary framework or tool in Java (other than Infinittest and other IDE tools). Nevertheless, you can use Guard to continuously test a Java project. I wrote about how to do it with an Android over two years ago and about 6 months ago I got involved in a Guard plugin for Gradle, dubbed Guard::Gradle.
To use Guard::Gradle, you’ll need to have Ruby installed. For those on OSX, you already have Ruby. Once you have Ruby installed, the installation of Guard::Gradle couldn’t be more easy: just open up a terminal in your desired Gradle project and type:
Simple installation!
$ curl https://raw.githubusercontent.com/bricker/guard-gradle/master/etc/installer
The above command will download a script and execute it. That script will:
- Install Ruby’s Bundler
- Install the Guard::Gradle plugin
- Create a default
Guardfile
- Create a Guard launcher script, dubbed
guard.sh
Therefore, after you run the above command, just execute guard.sh
to start Guard::Gradle! Any time you change a file in your source tree, a corresponding test will be executed using the Gradle test
task.
The default Guardfile
assumes a standard Gradle project setup, with source code located in src/main
. In fact, if you are curious, take a look at the generated Guardfile
and you’ll see:
Default Guard::Gradle Guardfile
guard :gradle do watch(%r{^src/main/(.+)\.*$}) { |m| m[1].split('.')[0].split('/')[-1] } end
That watch
command examines the src/main
directory and attempts to execute a matched file’s test. If no test is found, all tests are run. So either way, if a change happens, you’re covered with some amount of tests. which ostensibly updates your local directory with all upstream changes). Consequently, if upstream changes have broken your local branch, you’ll know instantly. No more need to remember to “run the tests” – they’re always running.
Of course, Continuous Integration isn’t a hack. But Ben made a prescient point back then: if you want to fail fast and shorten the time to detect failures, why not do it at the source? Why not know instantly while you’re working instead of some later time after you’ve moved on? Assuming your project has tests, then Continuous Testing is the answer. Continuous Testing is the proactive yin to CI’s reactive yang.
Check out Guard:Gradle today and know instantly when you’ve made a breaking change! Now that’s something everyone can dig, right?
Reference: | Continuous Integration Is a Hack! from our JCG partner Andrew Glover at the The Disco Blog blog. |
Tests shouldn’t be testing a single class in isolation must of the time, so adding ‘Test’ to the class name doesn’t work for me. Code coverage tools/libraries would be more suited to establish the mapping between code and tests.
For Java, Eclipse plugins that run tests after code changes exist, but I found they don’t work so well with multi-module projects.
Usually at my previous job, we ran the full range of tests before we checked in. We had merged our code with HEAD in Git in our local repo… ran the tests then checked in our code and tests were again run when the pull request was merged in.
So, Continuous Integration and Testing was successful. This may not work on all projects. But in the real time world we worked in, it did… It prevented or found problems before they were deployed to production and made the client happy with turn around time.
https://raw.githubusercontent.com/bricker/guard-gradle/master/etc/installer – NOT FOUND