About Me

Monday, September 19, 2011

Good unit tests

This article is a short description of the way how to create good unit tests.
I want to recomend the book "The Art Of Unit Testing" to every one who is facing with requirements to cover its code with unit tests. Its really great book that contains enough descriptions and examples (in C#) to write the correct tests.
What's the "correct" tests are? Related to the book, "correct" tests should follow the following requirements:
  • It’s an automated piece of code that invokes a different method and
    then checks some assumptions on the logical behavior of that
    method or class.

    Correct unit test should contain 3 small piece of code - Arrange-Action-Assert (AAA):
    [Test]
    public void SetCurrentSpeed_NewSpeedOfThePlaneIsBiggerThenMinimumTakeOffSpeed_PlaneCanTakeOff()
      {
       ///Arrange - prepare object that is going to be tested
       Plane plane = PlanesFactory.CreatePlane(Planes.Boeing747_400);
       int takeOffSpeed = 160;
    
       ///Action - execute method that's going to be tested
       plane.SetCurrentSpeed(takeOffSpeed);
    
       ///Assert - test the state of the object
       Assert.IsTrue(plane.CanTakeOff);
    
      }
  • It’s written using a unit-testing framework.
    Normally you can use NUnit framework for it. Most of companies I worked for used NUnit. But, there are other you can also use. The principles are the same everywhere.
  • It can be written easily.
    As you may see in example above, its not need to create big amount of lines of code. The code should contain minimum lines of code. In my experience, most of unit test code is taking Arrange part. Its taking a lot of lines to initialize all objects and prepare them for the execution.
    Using Moq library can significant reduce the number of lines you have to type to create stubs of the components you are going to use in the test.
    For instance, in the example below we are going to test, is factory creates correct plane:
    [Test]
      public void SetCurrentSpeed_NewSpeedOfThePlaneIsBiggerThenMinimumTakeOffSpeed_PlaneCanTakeOff()
      {
       ///Arrange - prepare object that is going to be tested
       Mock<iwings> moqWings = new Mock<iwings>();
       moqWings.Setup(f => f.WingSize).Returns(100);
    
       ///Our factory will create plane according to the size of its wings.
       /// The take off speed depends of the size of the wing
       Plane plane = PlanesFactory.CreatePlane(moqWings.Object);
       int takeOffSpeed = 160;
    
       ///Action - execute method that's going to be tested
       plane.SetCurrentSpeed(takeOffSpeed);
    
       ///Assert - test the state of the object
       Assert.IsTrue(plane.CanTakeOff);
    
      }
    
    As you may see from the example, the PlanesFactory takes an object that describes wings of the plane. Without using Moq, you would need to create test class that implements IWing interface, create instance, set the size of the wing to 100 and only after that pass it to the the PlanesFactory. Moq is making your life easier, the code less and much more easier to understand - it creates the instance of IFly interface on the fly for you.
  • It runs quickly.
    This is obvious, as more simple your test code, as more fast it working
  • It can be executed repeatedly by anyone on the development team.
    One of the purpose of writing unit tests is to make the life of your coworkers easier. When the guy from your team want to change your code or a code in a functionality that relates to the one you created, he need to be sure, that he did not break something.
    The guy is changing the code, then running all existing unit tests and saying "Woohoo! I did not break anything!". As more the code of your applicaiton is covered with the unit tests, as more free can feel the developer while adding a modifications to the code.
Things above are principles of writing unit tests.
Below, I going to specify few  important recommendations about the structure of the unit test.
They were also taken from the book with my short explanation.


  • It’s common practice to have one test class per tested class, one test
    project per tested project, and at least one test method per tested method.
    This advice related to the structure of your code. Everything is obvious here. To make the code clear and easy to read and modify, create a separate file for each testing class.
    In my opinion, sometimes, when you are writing a test to some manager class that contains a lot of methods independent from each other, its better to have one class for each testing method. In this case, its better to create a folder with the name of the manager class and put there all methods testing classes:
  • Name your tests clearly using the following model: [MethodUnderTest]_[Scenario]_[ExpectedBehavior].
    As you may see in example above, my test class is following to the requirement and has name "SetCurrentSpeed_NewSpeedOfThePlaneIsBiggerThenMinimumTakeOffSpeed_PlaneCanTakeOff".
    The reason of naming the method in this way is that its much easier to understand which tests did not pass, what functionality they do and where are they are locating.
    You may also be less strict in using the rule. The main idea of naming the test method is that  its name should follow if/then manner : "If_I_Put_New_Speed_Of_Plane_To_The_Value_That_Is_Bigger_Then_A_TakeOff_Speed_Then_CanTakeOff_property_should_be_set". This name can be also quite informative.
  • Use the [SetUp] and [TearDown] attributes to reuse code in your tests, such as code for creating and initializing objects all your tests use.
    If your test requires initialization of the DB or some complex object like COM, this staff should be placed to the [SetUp] method. The reason is simple - to keep the testing code as much simpler as possible.
    If you have few classes that doing the same in [SetUp] and [TearDown], create an abstract test class and move all logic in there. Then create your tests as inheritors of that base class.
    Don't confuse two attributes [SetUp] and [SetUpFixture]. [SetUp] is calling before EACH TEST in a test class and [SetUpFixture] is calling before FIRST TEST in a class will be called.
  • Don’t use [SetUp] and [TearDown] to initialize or destroy objects that
    aren’t shared throughout the test class in all the tests, because it makes
    the tests less understandable. Someone reading your code won’t
    know which tests use the logic inside the setup method and which
    don’t.

    Let's take a look on the example below:
    [TestFixture]
     public class PlaneTests
     {
      private iengine[] _enginesForTestCollection;
    
      [SetUp]
      public void SetUp()
      {
       Mock<iengine>() engine1 = new Mock<iengine>();
       engine1.Setup(f => f.Power).Returns(50);
    
       Mock<iengine>() engine2 = new Mock<iengine>();
       engine2.Setup(f => f.Power).Returns(99);
       
       _enginesForTestCollection = new IEngine[] {engine1.Object, engine2.Object};
      }
    
      [Test]
      public void SetCurrentSpeed_NewSpeedOfThePlaneIsBiggerThenMinimumTakeOffSpeed_PlaneCanTakeOff()
      {
       ///Arrange - prepare object that is going to be tested
       ...
       
       Plane plane = PlanesFactory.CreatePlane(moqWings.Object, _enginesForTestCollection[0]);
       int takeOffSpeed = 160;
    
       ///Action - execute method that's going to be tested
       plane.SetCurrentSpeed(takeOffSpeed);
    
       ///Assert - test the state of the object
       Assert.IsTrue(plane.CanTakeOff);
    
      }
    
      [Test]
      public void SetCurrentSpeed_NewSpeedOfThePlaneIsLessThenMinimumTakeOffSpeed_PlaneCanNotTakeOff()
      {
       ///Arrange - prepare object that is going to be tested
       ...
       Plane plane = PlanesFactory.CreatePlane(moqWings.Object, _enginesForTestCollection[1]);
       int takeOffSpeed = 120;
    
       ///Action - execute method that's going to be tested
       plane.SetCurrentSpeed(takeOffSpeed);
    
       ///Assert - test the state of the object
       Assert.IsFalse(plane.CanTakeOff);
    
      }
    
     }
    
    As you may see, I doing the thing that should not - initializing engines in the setup variable.
    I'm expecting, that my _enginesForTestCollection will have an engine for each test.
    Then, when I'm writing test, I'll use an appropriate engine.
    First problem with this approach is that when new guy, or even you, will read this code in the future, he will have to find, where is _enginesForTestCollection[1] is initializing and check the initial values of it. When your test class contains dozen of tests, it may be difficult.
    The second problem may come when you will want to write new test. In this case you will have a temptation to use one of existing engines. Then, if requirements will change and you will need to change the parameters of the engine, this change will affect two or more tests, depends of how many times you used the same engine. As result, some tests may not working or, what's more dangerous, some tests will remain success but they will not test things that were expected at the beginning.
    That's why its better to create each engine in its own test method and don't create unique moq for all tests.

No comments:

Post a Comment