简体   繁体   中英

How to write unit test first and code later?

I am new to unit testing and have read several times that we should write unit test first and then the actual code. As of now , i am writing my methods and then unit test the code.

If you write the tests first...

You tend to write the code to fit the tests. This encourages the "simplest thing that solves the problem" type development and keeps you focused on solving the problem not working on meta-problems.

If you write the code first...

You will be tempted to write the tests to fit the code. In effect this is the equivalent of writing the problem to fit your answer, which is kind of backwards and will quite often lead to tests that are of lesser value.

Sounds good to me. However, How do i write unit tests even before having my code in place? Am i taking the advice literally ? Does it means that i should have my POCO classes and Interfaces in place and then write unit test ?

Can anyone explain me how this is done with a simple example of say adding two numbers?

It's simple really. Red, Green, Refactor.

Red means - your code is completely broken. The syntax highlighting shows red and the test doesn't pass. Why? You haven't written any code yet.

Green means - Your application builds and the test passes. You've added the required code.

Refactor means - clean it up and make sure the test passes.

You can start by writing a test somewhat like this:

[TestMethod]
public void Can_Create_MathClass() {
    var math = new MathClass();
    Assert.IsNotNull(math);
}

This will fail ( RED ). How do you fix it? Create the class.

public class MathClass {
}

That's it. It now passes ( GREEN ). Next test:

[TestMethod]
public void Can_Add_Two_Numbers() {
    var math = new MathClass();
    var result = math.Add(1, 2);
    Assert.AreEqual(3, result);
}

This also fails ( RED ). Create the Add method:

public class MathClass {
    public int Add(int a, int b) {
        return a + b;
    }
}

Run the test. This will pass ( GREEN ).

Refactoring is a matter of cleaning up the code. It also means you can remove redundant tests. We know we have the MathClass now.. so you can completely remove the Can_Create_MathClass test. Once that is done.. you've passed REFACTOR , and can continue on.

It is important to remember that the Refactor step doesn't just mean your normal code. It also means tests. You cannot let your tests deteriorate over time. You must include them in the Refactor step.

When you create your tests first, before the code, you will find it much easier and faster to create your code. The combined time it takes to create a unit test and create some code to make it pass is about the same as just coding it up straight away. But, if you already have the unit tests you don't need to create them after the code saving you some time now and lots later.

Creating a unit test helps a developer to really consider what needs to be done. Requirements are nailed down firmly by tests. There can be no misunderstanding a specification written in the form of executable code.

The code you will create is simple and concise, implementing only the features you wanted. Other developers can see how to use this new code by browsing the tests. Input whose results are undefined will be conspicuously absent from the test suite

There is also a benefit to system design. It is often very difficult to unit test some software systems. These systems are typically built code first and testing second, often by a different team entirely. By creating tests first your design will be influenced by a desire to test everything of value to your customer. Your design will reflect this by being easier to test.

Let's take a slightly more advanced example: You want to write a method that returns the largest number from a sequence.

Firstly, write one or more units test for the method to be tested:

int[] testSeq1 = {1, 4, 8, 120, 34, 56, -1, 3, -13};

Assert.That(MaxOf(testSeq1) == 120);

And repeat for some more sequences. Also include a null parameter, a sequence with one element and an empty sequence and decide if an empty sequence or null parameter should throw an exception (and ensure that the unit test expects an exception for an empty sequence if that's the case).

It is during this process that you need to decide the name of the method and the type of its parameters.

At this point, it won't compile.

Then write a stub for the method:

public int MaxOf(IEnumerable<int> sequence)
{
    return 0;
}

At this point it compiles, but the unit tests fail.

Then implement MaxOf() so that those unit tests now pass.

Doing it this way around ensures that you immediately focus on the usability of the method, since the very first thing you try to do is to use it - before even beginning to write it. You might well decide to change the method's declaration slightly at this point, based on the usage pattern.

A real world example would apply this approach to using an entire class rather than just one method. For the sake of brevity I have omitted the class from the example above.

It is possible to write the unit tests before you write any code - Visual Studio does have features to generate method stubs from the code you've written in your unit test. doing it this way around can also help understand the methods that the object will need to support - sometimes this can aid later enhancements (If you had a save to disk, that you also overload to save to Stream , this is more testable and aids spooling over the network if required later on)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM