The Daily Parker

Politics, Weather, Photography, and the Dog

Another bit of sanity brought to you by unit testing

I just saved myself hours of pain by creating a unit test around a simple method that turned out to have a subtle bug.

The method in question calculates the price difference between two subscriptions for a product. If you're using the product, and you use more of it, the cost goes up. Every day, the application looks to make sure you're only using your allotted amount. If you go over, you get automatically bumped to the next subscription level and charged the difference, pro-rated by how much of the subscription term is left.

Here's the basic code:

var delta = subscription.IsAnnual ? 
   newTier.AnnualPrice - currentTier.AnnualPrice : 
   newTier.MonthlyPrice - currentTier.MonthlyPrice;

All well and good, except MonthlyPrice, for reasons known only to the previous developer, is nullable. So in order to prevent an ugly error message, I made sure it could never be null using the ?? operator:

var delta = subscription.IsAnnual ? 
   newTier.AnnualPrice - currentTier.AnnualPrice : 
   newTier.MonthlyPrice ?? 0m - currentTier.MonthlyPrice ?? 0m;

Do you see the problem? I didn't. But I learned today that - takes precedence over ??. So here's the correction:

var delta = subscription.IsAnnual ? 
   newTier.AnnualPrice - currentTier.AnnualPrice : 
   (newTier.MonthlyPrice ?? 0m) - (currentTier.MonthlyPrice ?? 0m);

I discovered that when the unit test I wrote kept insisting that 6 - 6 = 6. This is because without the parentheses where I put them, the compiler thinks I meant this:

newTier.MonthlyPrice ?? ((0m - currentTier.MonthlyPrice) ?? 0m)

In English, the compiler thought my first attempt meant, "Take the new monthly price, except if it's null, in which case take zero minus the old monthly price, unless that's null too, in which case take zero." What I meant, and what my correction means, is, "Take the new monthly price, or zero if it's null, and subtract the old monthly price (or zero if that's null)."

I'm glad I use NUnit.

Comments are closed