My Thoughts: Unit Test - Examples

I got a tweet from boltyk on twitter asking about a post on types of unit tests.

This post is from last summer where I rambled about types of unit tests. I was hoping for a stronger presentation of "Types of Unit Tests" when i started... but ... it didn't. I think the types I put up are OK; and they are types.
Hence why I started the "My Thoughts" type posts... I'm thinking out loud... ish.

boltyk's request for examples seems like a pretty good idea. I can't show a real system. Code I do for work is not available; and I haven't tracked examples to pull up (though I'll start).

I'm going to create stupid, simple, contrived examples and hope they help illustrate my thoughts some more.

Code Coverage Unit Test

Simple tests around ... getting code coverage.

Here's a simple class that I'm going to get 100% code coverage on; and have no idea if it works.

public class CodeCoverage
{
    public string SomeValue { get; set; }
    public int SomeOther { get; set; }

    public void MethodToSetSomeValue(int selector)
    {
        switch (selector)
        {
            case 1:
                SomeValue = "A";
                break;
            case 2:
                SomeValue = "BB";
                break;
            case 3:
                SomeValue = "CCC";
                break;
            case 4:
                SomeValue = "DDDD";
                break;
            default:
                SomeValue = "Yeppers";
                break;
        }
        SomeOther += SomeOther;
    }
}

A single test gives me 100% code coverage and I know NOTHING about how the method works.

[TestMethod]
public void ShouldUpdateSomeValue()
{
    for (int i = 0; i < 5; i++)
    {
        CodeCoverage codeCoverage = new CodeCoverage();
        codeCoverage.MethodToSetSomeValue(i);
        Assert.IsNotNull(codeCoverage.SomeValue);
    }
}

UnitTestExampleCodeCoverage

What does the class do? No idea. Did I break it? No idea. I may drop code coverage; but probably not gonna break the test.

This is a test that is just to get code coverage up.

Implementation Based Unit Tests

These are the tests that break whenever you change the implementation. They are fragile and not a developers friend.
I have a super simple class that does a few things; order doesn't matter; but in the test; It's happening the way I coded it. I'm going to ensure the implementation is what I wrote.

public class Implementation
{
    public void Foo(ISampleObject obj, IOtherThing ot)
    {
        obj.Foo();
        obj.Bar();
        ot.Value();
        ot.Source(obj.Boo());
    }
}

To test this; I bust out Mocking. ... shudder Mocking bad... boo mocking
and write this monstrosity.

[TestMethod]
public void ShouldVerifyImplementation()
{
    MockSequence mockSequence = new MockSequence();
    Mock<IFoo> mockFoo = new Mock<IFoo>();
    Mock<ISampleObject> mockSampleObject = new Mock<ISampleObject>(MockBehavior.Strict);
    Mock<IOtherThing> mockOtherThing = new Mock<IOtherThing>(MockBehavior.Strict);
    mockSampleObject.InSequence(mockSequence).Setup(o => o.Foo()).Verifiable();
    mockSampleObject.InSequence(mockSequence).Setup(o => o.Bar()).Verifiable();
    mockOtherThing.InSequence(mockSequence).Setup(o => o.Value()).Verifiable();
    mockSampleObject.InSequence(mockSequence).Setup(o => o.Boo()).Returns(mockFoo.Object).Verifiable();
    mockOtherThing.InSequence(mockSequence).Setup(o => o.Source(mockFoo.Object)).Returns("Something").Verifiable();

    string result = new Implementation().Foo(mockSampleObject.Object, mockOtherThing.Object);

    Assert.AreEqual("Something", result);
    mockSampleObject.VerifyAll();
    mockOtherThing.VerifyAll();
}

As an added bonus - 100% code coverage! Wooo!
... It hurts to look at. I don't know what it's doing and I wrote it 30 seconds ago...

It tests... that I wrote what I think I wrote... That's it.

All The Things Unit Test

This is where we test a whole bunch of operations from a single class.
Like these four calls in the //Act

[TestClass]
public class AllTheThingsTests
{
    [TestMethod]
    public void ShouldTestTheClass()
    {
        //Arrange
        Mock<IBar> mockBar = new Mock<IBar>();
        mockBar.Setup(o => o.Find(It.IsAny<int>())).Returns("result").Verifiable();
        AllTheThings allTheThings = new AllTheThings(mockBar.Object);

        //Act
        string bar = allTheThings.FindBar(1);
        string thing = allTheThings.FindThing(1);
        string golf = allTheThings.FindGolf(1);
        string game = allTheThings.FindGame(1);

        //Assert
        Assert.AreEqual("result", bar);
        Assert.AreEqual("result", thing);
        Assert.AreEqual("result", golf);
        Assert.AreEqual("result", game);
    }
}

We call multiple methods and assert multiple times. If this test fails... what broke?
Gotta dig into it and run the test; our output doesn't help much.
This is testing all the things in a single test.

My Ideal

My ideal of unit testing is going to be well represented in the Pizza Shop blogs. I write small tests, then write the simplest possible to get it to pass.

As a simple example; if I want to test that a method returns the string version of a number - My test will be

[TestMethod]
public void ShouldTestTheClass()
{
    new Stringer()
}

That's not a typo lacking the ;. I don't have a class Stringer. That's red.
Second Rule of TDD: You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
Then I make the class; and add the ;

[TestMethod]
public void ShouldExist()
{
    new Stringer();
}
public class Stringer { }

Then I write a test to implement the interface (because everything has an interface)

[TestMethod]
public void ShouldImplementIStringer()
{
    IStringer stringer = new Stringer();
}

IStringer doesn't exist. Make it exist. Still doesn't compile; new error - Stringer doesn't implement IStringer.
Now it does, now it all passes.
We're green - refactor. What ShouldExist tests is also tested in ShouldImplementIStringer - remove ShouldExist.
Let's make the method

[TestMethod]
public void ShouldReturnStringOfNumberOne()
{
    IStringer stringer = new Stringer();
    string actual = stringer.Stringify(1);
}

No assert yet because Stringify doesn't exist. I have a 1 there so I can be lazy when the method's created for me via Resharper
Make the method on the interface, make the method on the class - BECAUSE it's red.

[TestMethod]
public void ShouldReturnStringOfNumberOne()
{
    IStringer stringer = new Stringer();
    string actual = stringer.Stringify(1);
    Assert.AreEqual("1", actual);
}

There's our test - With an assert. It fails; due to the class being


public class Stringer : IStringer {
    public string Stringify(int i)
    {
        throw new System.NotImplementedException();
    }
}

but let's change the throw to return null. Then our test is failing on our assert. The best place to fail.

How do we make the test pass? We know we'll need to transform i into the string form... i.ToString()?

Wrong.

public class Stringer : IStringer {
    public string Stringify(int i)
    {
        return "1";
    }
}

Does our test pass? I don't care about anything but "simplest possible to make the test pass". Does this meet that? Then it is what we write.
I'm doing some refactors - 'cause we're green.
Moving to the next test

[TestMethod]
public void ShouldReturnStringOfNumberTwo()
{
    IStringer stringer = new Stringer();
    string actual = stringer.Stringify(2);
    Assert.AreEqual("2", actual);
}

Here I want to demonstrate a few practices for modifying code that isn't widely done. I get this mostly from Woody Zuill.

public class Stringer : IStringer {
    public string Stringify(int toString)
    {
        if(toString == 2) return "2";
        return "1";
    }
}

Doing this first allows us to make the test pass; but original code is unchanged.
When our tests pass we can refactor.

public class Stringer : IStringer {
    public string Stringify(int toString)
    {
        if(toString == 2) return toString.ToString();
        return "1";
    }
}

Continues to pass; we continue the changes.

public class Stringer : IStringer {
    public string Stringify(int toString)
    {
        return toString.ToString();
        return "1";
    }
}

Now the return "1"; isn't used (some languages will force it to be commented out). We still have the original functionality; so we can revert in a moment to a known good state.
Once this has all the tests passing; we can continue to refactor.

public class Stringer : IStringer {
    public string Stringify(int toString)
    {
        return toString.ToString();
    }
}

And now we're passing with our new logic. Let's refactor some more - What difference is there between ShouldReturnStringOfNumberOne and ShouldReturnStringOfNumberTwo? Basically nothing. The test the exact same code flow. They are duplicate tests.
Delete one; rename the other to the appropriate scope ShouldReturnStringOfNumber.

In far too many words - That's my ideal for writing unit tests.

Summary

I hope that helps with demonstrating what I was thinking with the My Thoughts: Unit Tests post.
Plus the detailed by the book flow of how I TDD.

Show Comments