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 the IsEmergency property.
  • VerifySet ensures that the setter was actually called during the execution of Process().
  • 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 the IsEmergency setter.
  • If Process() assigns a value, the IsSetCalled flag will be set to true.
  • 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 and after 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.