Testing assumptions with preconditions

While driving design with unit tests, I often break behaviors out into separate classes, both to increase cohesion, and as a side effect, increase testability.  Occasionally, I run into situations where I have some sort of environmental variable that never changes.  Or, if it does change, it would take an act of Congress.

When designing these environmental “astronomical constants”, I take the “JFHCI” approach.  These are values that have never changed, and the customer has told us don’t need to change.  One case I ran in to recently is a reward limit.  If you purchase over X dollars, you earn a reward.  The value X does not change, so I made it a constant:

public const decimal GadgetSpendRewardLimit = 200m;

My “testability” hat on says that constants are bad, and that I need to be able to properly set up all environmental variables.  However, in this case, allowing this value to change in ANY way produces a design that does not match reality.  In reality, this reward limit does not need to change.

Instead of going through a bunch of hoops to allow this value to change solely for testing, I’ll leave the value alone.  However, in the off chance that this value DOES change, my tests make the assumption that this value is this constant.

So, I’ve introduced testing assumptions made in my tests with a set of preconditions in the setup portion of the test:

public void SetUp()
    Debug.Assert(Customer.GadgetSpendRewardLimit == 200m, "Assumes threshold is $200");

I use a Debug.Assert mostly just to signify that I don’t need to test this every time, so I don’t really need a regular test assertion.  Instead, this is just a fail-safe, and matches the intent of assumption validation, rather than behavior validation.

With assumption checking through preconditions, I keep the correct design (an immutable constant), while providing explicit callouts of assumptions made about the “correctness” of my tests.  If my assumptions are wrong, I won’t bother trying to continue the test, as the base set of assumptions the test was built around are no longer valid.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • What would the downsides be to just using the constant in your tests, along the lines of result.ShouldEqual(Customer.GedgetSpendRewardLimit) rather than result.ShouldEqual(200m)? Then changing the constant’s value wouldn’t affect the tests.

  • I like the JFHCI approach for this type of thing. However, if the value is public I use “static readonly” instead of “const”. That at least gives me the freedom to change the value in the future without having to recompile all the consuming assemblies that might have used it.

  • I call these “booby traps” If you change some fundamental assumption of the system, it should trigger a tripwire and smack you in the fact so you have to think about the consequences.

    I like all my surprises up front and during development when I can deal with them properly vs. being surprised during a customer demo or live in production

  • @Darrell

    Quite a few times I’ll have calculations based on these constants, so I personally prefer not to duplicate the calculation logic to arrive at the expected result in my unit test. Otherwise, no real harm, and probably just a preference thing.


    I go back and forth on this one, trying to figure out what best conveys the intent of the design.

  • Have you considered Code Contracts? We’re not using them (yet), but I’ve also gotten pushback on using debug.assert instead of hard exceptions.

  • Harry Steinhilber

    I like to do the same thing for my tests, only using NUnit’s Assume.That() instead of a Debug.Assert(). It allows all the tests to fail with an inconclusive result to show my assumptions are wrong, not the test.

  • @Ryan

    I thought code contracts were only for method parameters? Here, it’s just a hard-coded const, how would I use code contracts?


    That’s pretty cool, I didn’t know about that piece! Thanks for the heads up!

  • I suspect the biggest assumption you’ve made is believing the customers original statement that the reward limit value has never changed and never needs to change!!

    Perhaps my experience hasn’t shaped my opinion enough to follow the JFHCI mantra at the moment but I’d be a little nervous about setting that value to a constant.

  • Michael Chandler

    @bogardj Code contracts allow for preconditions, postconditions and invariants. You /could/ use an invariant but they’re checked at the /end/ of all public method calls, not the start. Besides, the constant isn’t really a contract.

    I really like the idea of having the tests fail when the constant value is changed. It really makes you think and recognise what the implications for of making the change are.

  • @Martin

    Yep, it’s an assumption, but also the simplest thing that could possibly work. I still don’t introduce duplication, that would be bad. But I don’t really see the point of allowing this value to be configurable, when no one needs it to be. Unless it’s for non-functional requirements, I’m just not a fan of “just-in-case” development.