What to put in your tests (Assertion Patterns)

What to put in your tests -- or, patterns for your assertions

Components are a mix of state and behavior. Thus, when we test, we typically want to know:

  • Did the state of the object change as expected?
  • Did the behavior occur that I expected?

Sometimes we want to know one or the other; sometimes we want to know both.

Here are some "assertion patterns"  to help you get started. **The terms here are taken from the outstanding book "Test-Driven" by Lasse Koskela.

Resulting-State Assertion

The resulting-state assertion tests data. It says "I'm doing something to my object that will change that object's data, or 'state'. I'm going to test that the resulting state of my object is as I expect". A simple example is the common "bank account" or "transaction" example: You have two accounts, you transfer $20 from one account to another, and you test that the first account is 20 bucks shorter and the second account has that 20 bucks. Here's a different example, using a typical "User" object:

Resulting-State Assertion

Guard Assertion

The guard assertion is simply a slight variant on the resulting state assertion; typically, the difference is that toward the top of the test, before you get into the "guts" of your assertion(s), you check the object for some condition that you want to ensure exists before proceeding with the meat of your tests. Think of it as "If this condition isn't true, I want to fail right now because the rest of the tests don't matter". Usually the "guard" is just a simple assertion for equality, often to check that a "default" condition exists. In our example here, we're simply checking that our user object always starts without the privileges we're adding. IF the user object already had those privileges, then that means something tinkered with our object (perhaps indicating a problem in our setup function) and we got a problem here, Houston, and we want to fail right now.

Guard Assertion

Different instances, same data

The different-instances, same-data pattern is common in DAO testing. Essentially, we're asserting that two objects are different instances but contain the same data. In MXUnit, you can test for "different instance" by using the assertNotSame() assertion.

Different instances, same data

"Delta" Assertion

Sometimes you can't assert an absolute equality (like "My list is now 5 elements long"). Sometimes, you have to assert equality relative to some previous state. In the example below, imagine you're hooking into some scheduling mechanism (this would be for an integration test, for example). We don't know exactly what getTotalScheduled() will return at any given test run. Maybe it's 1. Maybe it's 30. Who knows. What we want to test is that when we schedule one additional thing, our scheduler's "totalScheduled" count increases by 1. Again, the result could be 2 or it could be 31. We don't know. This type of assertion, where we compare the state right before and right after performing some task, is called "delta", or difference, assertion.

Delta Assertion

And here's what it might look like for our User object:

Delta Assertion on User object

Interaction Assertion

With interaction assertions, we're testing to make sure an object and a collaborator worked together as we expected. A great example of an interaction is a "bean" style object, like perhaps a "User", and the DAO for that object, like a UserDAO:

Interaction Assertion

Testing for Expected Exceptions

Frequently, you want to test the "error paths" in your code. You want to ensure that functions throw Exceptions under certain conditions. To test this, use the mxunit:expectedException attribute on your test's cffunction tag:

ExpectedException

You can pass a list of expected exceptions in the expectedException attribute:

ExpectedException list

In CFScript:

ExpectedException in CFScript
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.