Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / unit-testing

Unit Testing - BDD, AAA Structure and Object Mocking

4.73/5 (13 votes)
8 May 2017CPOL6 min read 35.2K   147  
BDD, AAA Structure And Object Mocking in Unit testing
In this article, you will learn how to write the test method name, guideline for the naming convention, body structure inside the test method, and why you need object mocking in the test method for unit testing.

Unit Testing

Naming Convention Of Test Method

Traditional Principle Of Unit Test

One Test Method is written to test one and only one method; one Assert method should test only one expectation at a time.

In short, the principle says – “one function/method and one assert per test method”

So, let’s consider the below example:

Image 1

Comparing Traditional Principle to the Real World

Test Scenario

Verify the “GetSum” Method.

Test Cases
Positive Test Cases
  • TC1: Given positive values, should return expected result
  • Test Data-1: firstValue =5, secondValue =6
Negative Test Cases
  • TC2: Given zero values, should produce invalid argument message
  • Test Data-2: firstValue =0, secondValue =0
  • TC3: Given negative values, should produce invalid argument message
  • Test Data-3: firstValue =-5, secondValue =-6
Exceptional Test Cases
  • TC4: Given threshold limit values, should throw exception message
  • Test Data-4: firstValue =2147483647, secondValue =2147483647
Test Method Example

Now according to the traditional principle, let’s write the test method for the “GetSum”.

Image 2

Now according to the traditional principle, we have covered the Positive Test Case with “Test Data-1”. But what about negative and exceptional test cases??

How do we cover the negative and exceptional test cases with traditional principle??

Behavior Driven Development (BDD)

Why Do We Need BDD

If we want to cover all of the behaviors of our test cases according to our previous example, then we need to follow some technique; so that, we can write down all of the behaviors of the method. So, the BDD which is the technique, gives us the opportunity to fulfill all of the test cases with standard and readable naming convention. Many people, many minds. There are many techniques to write the naming convention of the test method. But it really depends on only you and your preference. There is nothing right or wrong - if you follow some other different technique. Anyway, in short, we can say that - in BDD, components test their expected behavior.

Concept Of BDD

  1. Given I am a beginner to the BDD technique, and I have never used this technique before
  2. When I read this tutorial for BDD
  3. Then I have started to like it and finally I learnt it

BDD Naming Convention

Test Scenario - Verify the “GetSum” Method

Test Cases - Positive Test Cases

  • TC1: Given positive values, should return expected result
  • Test Data-1: firstValue =5, secondValue =6

Test Method - Naming Convention

  • GivenPositiveVaidValuesAsParams_WhenGetSumIsCalled_ThenItShouldReturnSumValue

More Readable

  • Given_Positive_Vaid_Values_As_Params_When_GetSum_Is_Called_Then_It_Should_Return_Sum_Value

Image 3

Negative Test Cases

  • TC2: Given zero values, should produce invalid argument message
  • Test Data-2: firstValue =0, secondValue =0

Test Method - Naming Convention

  • GivenZeroValuesAsParams_WhenGetSumIsCalled_ThenItShouldThrowInvalidArgumentException

More Readable

  • Given_Zero_Values_As_Params_When_GetSum_IsCalled_Then_It_Should_Throw_Invalid_Argument_Exception
  • TC3: Given negative values, should produce invalid argument message
  • Test Data-3: firstValue =-5, secondValue =-6

Test Method - Naming Convention

GivenNegativeValues_WhenGetSumIsCalled_ThenItShouldThrowInvalidArgumentException

More Readable

Given_Negative_Values_When_GetSum_Is_Called_Then_It_Should_Throw_Invalid_Argument_Exception

Exceptional Test Cases

  • TC4: Given threshold limit values, should throw exception message
  • Test Data-4: firstValue =2147483647, secondValue =2147483647

GivenMaxLimitValuesOfIntAsParams_WhenGetSumIsCalled_ThenItShouldThrowSumException

More Readable

Given_Max_Limit_Values_Of_Int_As_Params_When_GetSum_IsCalled_Then_It_Should_Throw_Sum_Exception

Body Structure Of Test Method

There are no hard code rules for that. We generally follow the AAA structure because it’s easy to read and understand. The general AAA structure is given below for the Test method.

AAA - Structure

  1. Arrange
  2. Act
  3. Assert

Arrange

In this Arrange portion, we declare variables and create the instance of the objects of the classes.

Image 4

Act

The verified method is called in the portion. This is mainly used for passing the input parameters into the method and collecting the actual return result from the calling method.

Image 5

Assert

In this portion, we compare the actual output result of the calling method with the expected output result. The test is passed or failed depending on this Assert portion.

Image 6

Object Mocking For Unit Testing

Why Do We Need Mock

Suppose you need to test the behavior of one method and it has an external service or method within it. During the unit testing, we have to avoid these kind of external dependencies and the mock technique gives us the facility to avoid the behavioral test of the external method or service.

Why?? Because, according to the first principle, we all know that - "Test the logic of the class only, nothing else” Unit test NEVER uses - configuration settings, a database, logging, another application/service/file/network I/O.

Anyway, let’s see the example:

Image 7

This GetSum method has no external dependencies within it. So, it does not need object mocking.

Now, see the below example of the class and it has two dependencies and these are CheckingAccount and SavingAccount.

Image 8

Now, we want to verify the method “GetTotalMoneyByUserAccount” and it has two dependencies within it. So, to solve this kind of situation, we need object mocking.

Mock Framework

There are many object mock framework like Typemock Isolator, Rhino Mocks, Moq and NMock. You can use any one of them.

Requirement before Making Mock

  1. The class can’t be sealed.
  2. The method can’t be static; but if you need then use Adapter pattern.
  3. You can mock if Interfaces, Abstract methods or properties or Virtual Methods or properties on concrete classes.

Adding MOQ For Unit Testing Project

Image 9

Select your Unit Testing Project. Go to Reference> Click right button of the mouse and select “Manage NuGet Packages”. Now write down ‘Moq’ into the search textbox and finally install it to your project.

Image 10

Object Mocking Using MOQ

Image 11

In the above example, look at the line number 35 and 41; here first, we are creating instances of the object using Moq.

C#
Mock<IBankAccount> mockCheckingAccount = new Mock<IBankAccount>();

Mock<IBankAccount> mockSavingAccount = new Mock<IBankAccount>();

In the line numbers 38 and 44, we are implementing a dummy implementation and if the "GetMoneyByUserAccountNo" method is called, then it will return the 5 and 6. So, we don't need to know any internal logics of that.

C#
mockCheckingAccount.Setup(a => a.GetMoneyByUserAccountNo(userAccountNumber)).Returns(5);

mockSavingAccount.Setup(a => a.GetMoneyByUserAccountNo(userAccountNumber)).Returns(6);

Now in line number 48 which shows that we are injecting the mock objects into the class via constructor.

C#
SimpleMath simpleMath = new SimpleMath
                        (mockCheckingAccount.Object, mockSavingAccount.Object);

Secondly, in the line number 56 of the 'Act' portion, we are calling the verified method and the output result goes into the actualSumResult variable and finally in line number 59, which is giving us the actual output result.

C#
actualSumResult = simpleMath.GetTotalMoneyByUserAccount(userAccountNumber);

My main goal to introduce you to the object mocking world. So, this is just an example. You can use any of the mocking framework.

How to Call Order Of Test Methods using Moq Sequences

Download Moq.Sequences.dll from Github, then add Moq.Sequences.dll as a reference in your .NET project and add a using Moq.Sequences; in your test class. Or simply use "Manage NuGet Packages” Manager and type "Moq.Sequences" and search it. Finally, install it to your project and use it to your call as a reference.

Image 12

Moq.Sequences supports the following:

  • Checks order of the method calls, property gets and property sets
  • Allows you to specify the number of times a call is made before the next one is expected
  • Allows inter-mixing of sequenced and non-sequenced expectations
  • Thread safe – each thread can have its own sequence

Example for Calling Methods Sequentially

C#
[TestMethod]
public void Given_Valid_Data_For_Object_Model_When_TestMethod_
                  Is_Called_Then_It_Should_Be_Done_Successfully()
{
    ......
    ......

    //// Methods are called sequentially.
    using (Sequence.Create())
    {
        //// First, it is called
        Given_Valid_Template_Data_When_MethodSave_Is_Called_
                    Then_It_Should_Be_Saved_Successfully();

         //// Second, it is called
        Given_Valid_Template_Value_When_MethodUpdate_Is_Called_
                    Then_It_Should_Update_Template();

        ///// Third, it is called
        Given_Valid_Template_ID_When_DeleteTemplate_Is_Called_
                    Then_It_Should_Delete_Template();
    }

    ......
    ......
}
C#
[TestMethod]
public void Given_Valid_Data_For_Model_When_TestMethod_
            Is_Called_Then_It_Should_Be_Done_Successfully()
{
    ......
    ......
    ///
    using (Sequence.Create())
    {
        //// Methods are called sequentially.
        mockObject.Setup(x => x.MethodA()).InSequence();
        mockObject.Setup(x=>x.MethodB()).InSequence(Times.AtMostOnce());
        mockObject.Setup(x=> x.MethodC()).InSequence(Times.Once());
    }
    ......
    ......
}

Part -1 "Software Testing - Best Practice and Principles to Write Unit Testing"

History

  • 17th April, 2017: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)