Unit Testing: How to Verify That a Value Was Actually Set (Even If It’s Still False)
Setting the Stage: The Ticket Processor Problem
Let’s talk about a situation that might sound familiar if you’ve spent any time writing backend code — or if you’ve been on the receiving end of a production bug caused by a “default value” problem.
Imagine you have a TicketProcessor
class that handles incoming support tickets. One of the critical properties on each ticket is IsEmergency
. This is a bool
value that indicates whether the ticket is an emergency, and it drives key business logic, like how quickly the ticket needs to be resolved and whether it triggers alerts to the on-call team.
Here’s the catch: even if the ticket isn’t an emergency (and IsEmergency
remains false
), you still need to know that the value was explicitly set — because failing to set it might mean the processor skipped a step or missed part of its logic. In other words, the value should not simply “default” to false
; it should be actively set to false
(or true
) as part of the processing.
This leads to a tricky unit testing problem: how do you verify that a boolean value was actually set, even if the value remains false
?
The Problem with Default Values
Booleans in C# (and most other languages) default to false
if they aren’t explicitly set. That means a test like this won’t cut it:
Assert.False(ticket.IsEmergency);
This only tells us that the value is false
— but it doesn’t tell us how it got that way. Did the processor explicitly set the value to false
? Or was it simply left as the default value? That’s an important distinction when you’re validating that your business logic is being followed.
How to Write a Unit Test That Catches This
Let’s explore three solid ways to confirm that the TicketProcessor
explicitly set the IsEmergency
value — even if it ends up being false
.
✅ 1. Use a Spy or Mock to Detect Assignment
The easiest way to catch whether a value was explicitly set is to use a spy or a mock.
Here’s an example using Moq
, a popular C# mocking framework:
The TicketProcessor
Class:
public class Ticket
{
public bool IsEmergency { get; set; }
}
public class TicketProcessor
{
public void Process(Ticket ticket)
{
// Business logic to determine if the ticket is an emergency
ticket.IsEmergency = false;
}
}
The Test:
You can create a mock and use SetupSet
to track whether the value was explicitly assigned:
using Moq;
using Xunit;
public class TicketProcessorTests
{
[Fact]
public void ShouldExplicitlySetIsEmergency()
{
var mockTicket = new Mock<Ticket> { CallBase = true };
var processor = new TicketProcessor();
// Create a spy on the setter for IsEmergency
mockTicket.SetupSet(t => t.IsEmergency = It.IsAny<bool>()).Verifiable();
processor.Process(mockTicket.Object);
// Verify that the value was actually assigned (even if it stayed false)
mockTicket.VerifySet(t => t.IsEmergency = It.IsAny<bool>(), Times.Once);
}
}
✅ How This Works:
SetupSet
creates a spy on theIsEmergency
property.VerifySet
ensures that the setter was actually called during the execution ofProcess()
.- If the processor skips assigning the value (leaving it as the default), the test will fail.
✅ 2. Use a Custom Test Double or Wrapper
If you don’t have a mocking library available (or prefer not to use one), you can create a custom test double that intercepts the property assignment.
Testable Ticket
Class:
public class TestableTicket : Ticket
{
public bool IsSetCalled { get; private set; }
public override bool IsEmergency
{
set
{
IsSetCalled = true;
base.IsEmergency = value;
}
}
}
The Test:
[Fact]
public void ShouldSetIsEmergency()
{
var ticket = new TestableTicket();
var processor = new TicketProcessor();
processor.Process(ticket);
// Check if the value was set
Assert.True(ticket.IsSetCalled);
}
✅ How This Works:
TestableTicket
overrides theIsEmergency
setter.- If
Process()
assigns a value, theIsSetCalled
flag will be set totrue
. - If the processor skips assigning a value, the test will fail.
✅ 3. Track the Value Before and After
Another option (if you don’t have control over the object or can’t create a mock) is to capture the value before and after execution.
Example Test:
[Fact]
public void ShouldSetIsEmergency()
{
var ticket = new Ticket();
var processor = new TicketProcessor();
bool before = ticket.IsEmergency;
processor.Process(ticket);
bool after = ticket.IsEmergency;
// Test passes if the value was explicitly changed OR if it remained false but was set
Assert.True(before != after || after == false);
}
✅ How This Works:
- Capturing
before
andafter
states will confirm that the value changed OR was explicitly set. - This test is less direct than using a spy, but it can still help catch logic errors.
Best Approach?
Here’s a quick breakdown of which approach to use:
Approach | Best For | Pros | Cons |
---|---|---|---|
Mocking/Spying | Clean, direct testing | Simple and clear | Requires a mocking library |
Custom Test Double | No mocking libraries or complex objects | No dependencies | Extra code |
Before/After Tracking | Simple checks | No need for additional libraries | Harder to detect intentional sets |
For most cases, using a mock or spy is the cleanest and most direct way to confirm the value is actually set — even if it stays false
. If you don’t have access to a mocking framework, a custom test double works well too.
Why This Matters
You might be wondering — why does this even matter if the final value is the same?
Because default values can mask logic bugs. If a value defaults to false
but your processor is supposed to make an explicit decision about that value, you want to know that the decision happened. Skipping that step could mean part of your business logic isn’t running correctly, which could cause hard-to-find bugs down the line.
When you write tests that confirm a value was explicitly set, you’re verifying that the logic actually happened — not just that the outcome looks right.
Final Thought: Write Tests That Catch Hidden Bugs
Unit tests should not just confirm that the output is correct — they should also verify that the right logic happened to create that output. Adding tests that confirm values were explicitly set (even when they stay false
) is a small but powerful way to make your tests stronger and your code more resilient.