The First Unit Test – Act-Assert-Arrange

I’ve written before about the process of writing the first unit test. I want to give an example of how you’re actually doing it, and I hope that after you read this, you’ll understand the strange title.

Let’s start with the canonical example: The closing form. Let’s say I have a Windows form. And I want to test what happens when I close it. Here’s the code for the closing event:

private void IsolatorTestForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (IsDirty)
{
var result = MessageBox.Show("Do you really want to close the window?",
"Confirm Closing",
MessageBoxButtons.OKCancel);
e.Cancel = (result == DialogResult.Cancel);
}
}

public bool IsDirty
{
get { throw new NotImplementedException(); }
}

So let’s start. The scenario I want to test is what happens when I close the form, after data has changed in the form and the user decided not to close the form. If you look closely, you can identify two more scenario to test – can you identify them?

Ok, first step: naming. Here’s the test name:

[TestMethod]
public void WhenClosing_DataHasChangedAndUserChoseToCancel_DontCloseForm()
{
// Arrange

// Act

// Assert
}

The name consists of three parts: What I’m testing, the specific scenario, and the expected result, all separated by underscores. Next I’m going to fill in the rest of my test, inside the Act and Assert parts.

[TestMethod]
public void WhenClosing_DataHasChangedAndUserChoseToCancel_DontCloseForm()
{
// Arrange
var form = new IsolatorTestForm();

// Act
form.Show();
form.Close();

// Assert
Assert.IsTrue(form.Visible);
}

Technically, creating the form object is part of the Act, but to be consistent with the next steps, I’ll leave it in the Arrange part, knowing the real story.

Ok, we have a test. Let’s run it. What happens?

image

We get an exception, thrown from IsDirty. To circumvent that, we’ll add a statement to our Arrange that deals with this. In our scenario, IsDirty should be True, since it reflects that the data has changed. The test now looks like this:

[TestMethod]
public void WhenClosing_DataHasChangedAndUserChoseToCancel_DontCloseForm()
{
// Arrange
var form = new IsolatorTestForm();
Isolate.WhenCalled(() => form.IsDirty).WillReturn(true);

// Act
form.Show();
form.Close();

// Assert
Assert.IsTrue(form.Visible);
}

Let’s run the test again. This time, the test hangs. Well, not really. There’s a message box somewhere waiting for someone to press the buttons. This comes from the event handler as well.

image

Obviously we don’t want message boxes popping up in our automated tests. In our scenario, the user decided to press Cancel, so we’re going to add a statement for that. The test now looks like this:

[TestMethod]
public void WhenClosing_DataHasChangedAndUserChoseToCancel_DontCloseForm()
{
// Arrange
var form = new IsolatorTestForm();
Isolate.WhenCalled(() => form.IsDirty).WillReturn(true);
Isolate.WhenCalled(()=> MessageBox.Show(null,null,MessageBoxButtons.OK)).WillReturn(DialogResult.Cancel);

// Act
form.Show();
form.Close();

// Assert
Assert.IsTrue(form.Visible);
}

Note that I picked the correlating overload of MessageBox.Show inside the event handler, and I gave the compiler enough to approve my code. Since I don’t care about the arguments when I change behavior, I can pass whatever I want, as long as the compiler is happy.

Let’s run the test again. Is third time the charm? YES!! The test passes! Play Ode To Joy!

This iterative scenario works. We start with what we want to test, and the bare-bones test, including the assertion. With every iteration, we bypass another problem, and we go back to the scenario we want to test. We end up with a working test. It’s also a good way to learn Isolator’s APIs.

Try it. I’d like to hear about your experience.

  • ulu

    Would be great to demonstrate the same in a test-first way. The calls for IsDirty and MessageBox.Show are actually parts of the requirements, so they would appear right from the start.

    Changing the test (as opposed to changing the code) in order to make the test pass is a bit.. unusual (although sometimes I’m doing it myself).

  • Gil Zilberfeld

    Artem,

    What I found, is that you somehow return to the test eventually. It could be because of refactoring, or the wrong overload. It’s hard to anticipate the Arrange part that includes the faking statements.

    It does feel a bit awkward, but on the other hand, you don’t feel like you’re bumping into a wall. You keep adding stuff until it works.

    And for the first tests, I think that’s a big plus.

  • ulu

    Yes, totally agree. I was just suggesting a topic for the next post..

  • Gil Zilberfeld

    I might just do it!

  • Shakira Weber

    Nice ideas . I loved the analysis ! Does someone know if my company can grab a blank 2011 VAR Form 300 example to type on ?

TOP