Codementor Events

Testing, and writing testable code. Part 2

Published May 02, 2018

In my experience there are 3 kinds of tests you can write. A unit test is a test that inspects a method by disconnecting that method and it's class from the rest of the code. An integration test will test one part of the software from top to bottom. A system test is designed to measure an entire process. This article will focus on unit testing.

Writing testable code

In my first article I talk about why you might want to use dependency injection. One of the other benefits of DI is that it makes writing tests so much easier.

I am going to borrow that example from myself. Recall the first example we looked at.

public class Recipes{
  public List<Recipie> SearchRecipies(string searchString){
    var matches = new List<Recipie>();
    var spreadSheetReader = new SpreadSheetReader("recipies.xls");
    ...
    var textFileReader = new TextFileReader("recipies/*.txt");
    ...
    var dbReader = new TableReader("companyDB","dbo.Recipies");
    ...
    return matches;
  }
}

This is going to be hard to test because the resources are declared and consumed inline. When writing a unit test you don't want there to be any side effects. Your tests should not be invasive. In this scenario you will need to find a way to feed the test a fake spreadsheet, a fake folder of text files, and a fake database.

If we were testing the DI version of Recipes instead, the one that looks like this.

//Recipes.cs
public class Recipes{
  private List<IRecipeSearcher> _searchers;
  public Recipes(List<IRecipeSearcher> searchers){
    _searchers = searchers;
  }
  public List<Recipie> SearchRecipies(string searchString){
    var matches = new List<Recipie>();
    foreach(var searcher in _searchers){
      matches.AddRange(searcher.SearchRecipes(searchString);
    }
    
    return matches;
  }
}

We have the ability to mock out the dependencies. The list of searchers is the dependency. For each searcher we are calling SearchRecipes. The Recipes class does not care what is in it's searchers.

My personal preference for Mocking out things is the moq library. It's dead simple to use. I don't like to use specific frameworks when I am explaining things but this really is my favorite.

[TestMethod]
public void SearchRecipies_ReturnsListOfRecipies(){
  // Arrange
  
  // Create a mock of a searcher. 
  // We can spy on and simulate the behavior of it's public methods.
  var mockSearcher = new Mock<IRecipeSearcher>();
  
  // Setup the Search recipes method. We don't care what the input is,
  // but we care about the output, since it will be used by the method.
  mockSearcher.Setup(s => s.SearchRecipes(It.IsAny<string>())
    .Returns(List<Recipe>{new Recipe{Title="Muffins"
      }});
  
  // Create an instance of the object to be tested and pass in the mocked objects.
  var testRecipes = new Recipes(new List<IRecipeSearcher>{mockSearcher.Object});
  
  // Act
  
  // Invoke the method to be tested.
  var result = testRecipes.Search("Muffin");
  
  // Assert
  
  // The Searcher's SearchRecipes method should have been called.
  mockSearcher.Setup(s => s.SearchRecipes(It.IsAny<string>()).Verify();
  
  // The result should have a list containing an element with a title 
  // of "Muffins".
  Assert.IsTrue(result.Any(x => x.Tile == "Muffins"));
}

Noteworthy items

  • Arrange, Act, Assert. Setup the test, perform the operation to get a result, and assert that the result is what is desired.

  • The dependencies are mocked in such a way that they are easily digestable. For the purposes of testing the Recipes class we don't care about the guts of any of the recipe searchers. That should be handled by the tests for those particular classes.

Discover and read more posts from Joel Longanecker
get started
post commentsBe the first to share your opinion
Show more replies