Unit Testing

Unit Testing

Unit testing is testing done at modular level (ie class, method...) and it ensures module integrity during:
  • Initial development
  • Bug fixing
  • Refactoring
The 'unit', should be isolated (solitary) and the unit test should check that the module does whatever specifications that are required of the module.
I'm going to start with a very simple example and then investigate the implications as the example becomes more complicated.
We'll start with a class Arithmetic and I only require it to multiply two numbers (a and b):
class Arithmetic {
    // a method that multiplies two numbers (a and b)
    // pre: a and b are Integers <Int32.Max and >Int32.Min
    // post: result a * b = Integer <Int32.Max and >Int32.Min
    public int MultiplyTwoNumbers(int a, int b) {
        return a + b; // a bug!
    }
}
The code to test this is:
[TestClass]
public class ArithmeticTest {
    // basic test that arithmetic returns correct result
    [TestMethod]
    public void Basic_correct_result() {
        Arithmetic arithmetic = new Arithmetic();
        int a = 10, b = 9;
        int expected = a * b, actual = 0;

        actual = arithmetic.MultiplyTwoNumbers(a, b);

        Assert.AreEqual(a, b);
    }
}
Which fails as I have a bug, instead of '*' I typed '+' and so the unit test has done it's job (so far).
I fix the problem then run my test and it fails again! My test is wrong the Assert.AreEqual(a, b) is incorrect so I change this to read Assert.AreEqual(expected, actual) and this now passes.
The code is now:
class Arithmetic {
    // a method that multiplies two numbers (a and b)
    // pre: a and b are Integers <Int32.Max and >Int32.Min
    // post: result a * b = Integer <Int32.Max and >Int32.Min
    public int MultiplyTwoNumbers(int a, int b) {
        return a * b;
    }
}
and the test is now:
[TestClass]
public class ArithmeticTest {
    // basic test that arithmetic returns correct result
    [TestMethod]
    public void Basic_correct_result() {
        Arithmetic arithmetic = new Arithmetic();
        int a = 10, b = 9;
        int expected = a * b, actual = 0;

        actual = arithmetic.MultiplyTwoNumbers(a, b);

        Assert.AreEqual(expected, actual);
    }
}
Is there anything else I could test, maybe I could test the max values? So I devise this test:
// a test to test if two maximum values return the correct result
[TestMethod]
public void Two_Maximum_Values() {
    Arithmetic arithmetic = new Arithmetic();
    int a = int.MaxValue, b = int.MaxValue;
    int expected = a * b, actual = 0;

    actual = arithmetic.MultiplyTwoNumbers(a, b);

    Assert.AreEqual(expected, actual);
}
Now this PASSES but it's clearly wrong but what result returned (it's 1), surely I should have got some sort of overflow.
Now this is an example of a false-positive, that is, I expected my test to pass, but I know it should fail.
You may be wondering what I'm trying to show here and it's really that unit tests can lead to more knowledge of the problem.
Clearly my specification of the method is incorrect, I want it to return the correct result not what the software decides is the correct result.
Ok so I change my specifications and my code becomes:
class Arithmetic {
    // a method that multiplies two numbers (a and b)
    // pre: a and b are Integers <Int32.Max and >Int32.Min
    // post: result a * b = long <Int64.Max and >Int64.Min

    public long MultiplyTwoNumbers(int a, int b) {
        return (long)a * (long)b;
    }
}
Which passes my tests but I had to change one line, in my test from
int expected = a * b, actual = 0;
to
long expected = (long)a * (long)b, actual = 0;
and I'm happy and can now continue developing the rest of my application.

The next part of my 'application' is a class that takes two lists of numbers and uses the Arithmetic.MultiplyTwoNumbers method on each pair.
And so, I build my new class:
public class BoostList {

    public static List<long>Blender(List<int> firstList, List<int> secondList) {
        List<long> blendedList = new List<long>();
        Arithmetic arithmetic = new Arithmetic();

        for(int i=0; i < firstList.Count; i++) {
            blendedList.Add(arithmetic.MultiplyTwoNumbers(firstList[i], secondList[i]));
        }

        return blendedList;
    }
}
The initial test I write which passes is:
[TestClass]
public class BoostListTest {

    [TestMethod]
    public void Multiplies_Adjacent_Numbers() {
        List<int> firstTestList = new List<int> { 1, 2, 3, 4 };
        List<int> secondTestList = new List<int> { 5, 6, 7, 8 };
        List<long> expected = new List<long> { 5, 12, 21, 32 };
        List<long> actual = new List<long>();

        actual.AddRange(BoostList.Blender(firstTestList, secondTestList));

        CollectionAssert.AreEqual(expected, actual);
    }
}
My next set of tests would check that the null valued lists could be handled correctly, but we'll ignore this for now.

What's more interesting is that I can't change the method arithmetic.MultiplyTwoNumbers and my test is directly coupled to this method!
You may be wondering, 'where?' well I directly calculated the results in List<int> expected and so, have indirectly coupled this test logic (bit of a mind bender!).
In other words, if I change my method logic then I'd have to change my unit test logic and if this was a complex application then there could be hundreds of tests (believe me I seen and done this).

How do I get around this? Well I use a mock or fake or one of the other miriad of terms which Martin Fowler uses the lovely general term test double.

So, I inject into the method Blender a mock of the function that I can control to prevent tight coupling.
I add the injected function into the method arguments and use this function for my number blending:
public class BoostList {

    public static List<long>Blender(List<int> firstList, List<int> secondList, Func<int, int, long> blendFunction) {
        List<long> blendedList = new List<long>();

        for(int i=0; i < firstList.Count; i++) {
            blendedList.Add(blendFunction(firstList[i], secondList[i]));
        }

        return blendedList;
    }
}
And my test now becomes:
[TestClass]
public class BoostListTest {

    [TestMethod]
    public void Multiplies_Adjacent_Numbers() {
        List<int> firstTestList = new List<int> { 1, 2, 3, 4 };
        List<int> secondTestList = new List<int> { 5, 6, 7, 8 };
        List<long> expected = new List<long> { 5, 12, 21, 32 };
        List<long> actual = new List<long>();
        Func<int, int, long> mockMergingFunction = (firstNumber, secondNumber) => (long)(firstNumber * secondNumber);

        actual.AddRange(BoostList.Blender(firstTestList, secondTestList, mockMergingFunction));

        CollectionAssert.AreEqual(expected, actual);
    }
}

Summary

The idea of this short article is to show that unit tests can lead to a better more flexible design, by reducing coupling between modules.
Also unit tests can show design flaws and problems inherent in language semantics which need attention.

References

wiki
guru99 Unit Testing
tutorialspoint Unit Testing
Martin Fowler Unit Testing
Moq
APOSD Unit Testing page 154