Lesson Wednesday

In the last lesson, we covered static variables. Static variables can be very helpful for temporary persistence. In other words, we can use them to store (or persist) information for a short period of time. However, they can cause a problem with tests. Let's illustrate this problem and posit a solution in our To Do List application by adding a new test for the static GetAll method we created in the last lesson.

First, let's add a Test Method to test that our GetAll method returns an empty list:

...

[TestMethod]
public void GetAll_ReturnsEmptyList_ItemList()
{
  // Arrange
  List<Item> newList = new List<Item> { };

  // Act
  List<Item> result = Item.GetAll();

  // Assert
  CollectionAssert.AreEqual(newList, result);
}

...
  • We arrange necessary components by creating newList, an empty List for holding Items.

  • We act by calling a static GetAll() method on the Item class. The return value from this method is stored in the variable result.

  • We assert that the result should be equal to newList. This will confirm our GetAll() method successfully returns a list.

To use List objects, we also need to add the using directive System.Collections.Generic; to the top of our file.

Now let's run our test. Our new test should fail with the following error:

Failed   GetAll_ReturnsEmptyList_ItemList
Error Message:
 CollectionAssert.AreEqual failed. (Different number of elements.)
Stack Trace:
   at ToDoList.Tests.ItemTests.GetAll_ReturnsEmptyList_ItemList() in /Users/epicodus_staff/ToDoList.Solution/ToDoList.Tests/ModelTests/ItemTests.cs:line 52


Total tests: 4. Passed: 3. Failed: 1. Skipped: 0.
Test Run Failed.
Test execution time: 1.1018 Seconds

Any ideas about what may be happening here?

Well, because the tests will run in order, by the time it gets to our final GetAll test, we've already added Items to our static list. Remember that each time a new Item object is created by the constructor, the entire item object itself is added to the static List of Items. This is causing the List<Item> result = Item.GetAll(); line to set result to a list containing items created in earlier tests instead of the empty list we expect.

Adding a Teardown Method


We can solve this problem with a teardown method. A teardown method will reset _instances data between each test, ensuring one test's results aren't affected by earlier tests.

We'll add the IDisposable keyword to our ItemTests class declaration. Let's also add the necessary using System directive if we don't already have it as IDisposable is defined in the System namespace:

ToDoList.Tests/ItemTests.cs
using System;
...

[TestClass]
public class ItemTests : IDisposable

...

IDisposable is what is referred to as an interface. We'll cover interfaces later in this section, but for now, just be aware that the : syntax above extends functionality from IDisposable to our ItemTests class.

Specifically, we'll be able to create a special function called Dispose() that resets data between tests. Let's add this function now:

ToDoList.Tests/ItemTests.cs
...

namespace ToDoList.Tests
{
  [TestClass]
  public class ItemTests : IDisposable
  {

    public void Dispose()
    {
      Item.ClearAll();
    }

    //Our tests
  }
}

Dispose() is a special method. Any code we add to Dispose automatically runs after every test. In our case, we'll call ClearAll() on the Item class. However, ClearAll() isn't a built-in method. We'll have to define this one ourselves. We'll add the following to our Item class below GetAll():

ToDoList.Solution/ToDoList/Models/Item.cs
...

public static void ClearAll()
{
  _instances.Clear();
}

...

Our ClearAll() method is static because it affects all Items in the class, not just one. We use the built-in List method Clear() to empty _instances.

All our tests should pass now. Great!

Going forward, remember that whenever we use static data, we need to create a Dispose() method to clean up between each tests, and that we'll need to have our test classes extend from the IDisposable interface.

Lesson 17 of 20
Last updated April 14, 2022