Thursday, January 03, 2013

Clojure: Expectations Warn On State Change During Test Runs

While writing tests it can be easy to accidentally change any global state that exists in your application. I've previously written about Redefining State Within a Test; however, redef-state and with-redefs only help you if you redefine all of the affected state. The situation is even more problematic due to the fact that accidental state alteration often doesn't cause issues until a completely unrelated test suddenly fails. After being bitten by this issue a few times, I added (to expectations) the ability to warn when global state is modified by a test.

As of version 1.4.24 if you add (expectations/warn-on-iref-updates) anywhere, then expectations will provide you with a warning whenever any global state is altered.

While you can add that snippet anywhere, I prefer to add it to the expectations Before Run Hook. There's an example expectations_options.clj in the expectations codebase that shows all of the code you need to enable this feature. Simply add this file or add the function to your existing file and you should see a warning on any global state alteration. If you're not sure where to put this file, refer to the Before Run Hook blog post.

Here's an example warning (generated by running the expectations tests).
WARNING: success.success-examples:280 modified #'success.success-examples-src/an-atom from "atom" to "another atom"
The warning should let you know which test is doing an unexpected modification, and the to and from values should give you an idea of where in the source the alteration is occurring.

Wednesday, January 02, 2013

Clojure: Expectations Before Run Hook

The expectations library now supports calling an arbitrary number of custom functions before the test suite is run.

There are several reasons that you might want to call a custom function before the test suite is executed-
  • load sample data to a database
  • delete a temp directory
  • replace logging with standard out or /dev/null

As of version 1.4.24, expectations will execute any function that includes the following metadata {:expectations-options :before-run}. The following example should serve as a decent reference on how to add your own 'before-run' functions.


These functions can be defined anywhere; however, expectations looks in a default location for a configuration file. If expectations finds this configuration file it removes (what should be) the namespace and requires it with :reload. As a result, this configuration namespace will be removed and redefined with each expectations suite run.

The name of this default configuration file is expectations_options.clj (thus the namespace is expectations-options). Expectations looks for this file in the root test directory. If you have :test-paths ["test/clojure"] in your project.clj, then you'll want to create test/clojure/expectations_options.clj.

If you place your expectations_options.clj file in the correct location and you add the {:expectations-options :before-run} metadata to a function in the expectations-options namespace, your function should be run automatically the next time your test suite runs. You may want to start with a (println) just to verify that things are going as you expect.

Wednesday, November 14, 2012

Clojure: Converting scenarios With Interleaved expect Calls To Bare expectations

Since I've deprecated scenarios, I went through all of my projects and removed any usages of expectations.scenarios. For the most part the conversion was simple; however, I did run into one instance where the scenario contained interleaved expectations.

The following code is an example of a scenario with interleaved expectations.


In the previously linked blog entry I recommend using a clojure assert to replace the interleaved expectations. That solution works, but I found an additional approach that I wanted to share.

When I encountered code similar in structure to the code above, I immediately envisioned writing 3 expectations similar to what you find below.


note: for my contrived example the first two tests could have been written without the let; however, the tests from my codebase could not - and I believe the blog entry is easier to follow if the tests are written in the way above.

While these tests verify the same expectations, the way that they are written doesn't convey to a test maintainer that they relate to each other more than they are related to the other tests within the file. While pondering this complaint, I grouped the tests in the following way more as a joke than anything else.


I would never actually use given simply to group code; however, grouping the code together did cause me to notice that there was a usage of given that would not only keep the code grouped, but it would also allow me to test what I needed with less code.

The following example is very similar in structure to the finished product within my codebase.

The above example verifies everything that the original scenario verified, does not use a scenario, and conveys to a maintainer that related logic is being tested within all three tests - in short: this felt like the right solution.

Tuesday, November 13, 2012

Elisp: Duplicate Line

After switching to emacs I quickly noticed that I was missing a simple keystroke for duplicating a line. Yes, I know it's as easy as C-a, C-k, C-y, Enter, C-y. Still, I wanted a keystroke. So, I coded up the following snippet. It's similar to what you'd find elsewhere on the net, but it also moves the cursor to where I've gotten used to it ending up.

Thursday, November 08, 2012

Elisp: Automated Switching Between Clojure Test and Source Files

The majority of the work that I do in emacs is Clojure programming. The Clojure navigation support (M-.) is usually all I need, but one thing that I find myself doing manually fairly often is jumping between tests and source. After suffering manual navigation for a few days, I finally automated the task using the Elisp from this blog entry.

All of my Clojure projects use both Leiningen and expectations; therefore, my directory structures always look similar to what you see below.
source - /Users/jfields/src/project-name/src/clojure/
tests - /Users/jfields/src/project-name/test/clojure/expectations/

Since my projects follow this convention, I'm able to make several assumptions about where the expectations and where the source will actually live. If you don't use expectations, or you follow a slightly different directory structure, you'll want to hack this a bit to follow your conventions.

If you're in a source file, find (and open) the expectations.


If you're in a test file, find (and open) the source.