Unit Test Practice

Unit Test Practice

tags: NTU_ST Software Testing

:::spoiler Click to open TOC [TOC] :::

Install the environment

  • Install Visual Studio with .NET that we can use C# language to implement unit test properly.install c#

Create a project to test

  • Create a new project and choose C# as your language and named the project Bank.create project
  • Rename Program.cs as BankAccount.cs and replace all content by the following code. ```c# using System;

namespace BankAccountNS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class BankAccount
{
    private readonly string m_customerName;
    private double m_balance;

    private BankAccount() { }

    public BankAccount(string customerName, double balance)
    {
        m_customerName = customerName;
        m_balance = balance;
    }

    public string CustomerName
    {
        get { return m_customerName; }
    }

    public double Balance
    {
        get { return m_balance; }
    }

    public void Debit(double amount)
    {
        if (amount > m_balance)
        {
            throw new ArgumentOutOfRangeException("amount");
        }

        if (amount < 0)
        {
            throw new ArgumentOutOfRangeException("amount");
        }

        m_balance += amount; // intentionally incorrect code
    }

    public void Credit(double amount)
    {
        if (amount < 0)
        {
            throw new ArgumentOutOfRangeException("amount");
        }

        m_balance += amount;
    }

    public static void Main()
    {
        BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99);

        ba.Credit(5.77);
        ba.Debit(11.22);
        Console.WriteLine("Current balance is ${0}", ba.Balance);
    }
} } ``` * Build it by clicking **Build Solution** in Build menu.

Create a unit test project

  • Create a new project at solution explorer and named it BankTests. The other part just maintain default setup.
  • Select MSTest Test Project<-This is important.
  • Add reference by selecting Add Reference at BankTests/Dependencies
  • In the Reference Manager dialog box, expand Projects, select Solution, and then check the Bank item.

Create the test class

  • Rename UnitTest1.cs to BankAccountTests.cs and replace the original code with the following section and add using statement at the top of the class file. ```c# using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BankTests { [TestClass] public class BankAccountTests { [TestMethod] public void TestMethod1() { } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
* BTW, the default class part can be ignored.

## Create the first test method
* Replace the default class with the following code
```c#
[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 4.55;
    double expected = 7.44;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    account.Debit(debitAmount);

    // Assert
    double actual = account.Balance;
    Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}
  • Analyze this part: Assert.AreEqual method will catch the exception when expected value unequal actual value or their difference larger than delta(0.001).
  • Comparing with BankAccount.cs, the truly process of Debit function is $beginningBalance - debitAmount = expected$ -> $11.99 - 4.55 = 7.44$

Build and run the test

  • On the Build menu, choose Build Solution (or press Ctrl + SHIFT + B) and press Ctrl + E, T to open Test Explorer, then Run All.
  • You’ll find that all Test are failed, so you must modify somewhere incorrect.

Fix your code and rerun your tests

  • Return to BankAccount.cs and observe Debit() function.
  • Must change m_balance += amount; to m_balance -= amount;
  • Build and Run the code again and you’ll find the test is correct this time

Create and run new test methods

  • Add the following program in test class and rebuild it. ```c# [TestMethod] public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange() { // Arrange double beginningBalance = 11.99; double debitAmount = -100.00; BankAccount account = new BankAccount(“Mr. Bryan Walton”, beginningBalance);

    // Act and assert Assert.ThrowsException(() => account.Debit(debitAmount)); }

[TestMethod] public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange() { // Arrange double beginningBalance = 11.99; double debitAmount = 200.00; BankAccount account = new BankAccount(“Mr. Bryan Walton”, beginningBalance);

1
2
// Act and assert
Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount)); } ``` * Analyze: you'll find out that the **debitAmout** in the **1st test method** is less than zero and the **debitAmount** in **2nd test method** is larger than **beginningBalance**. * After runing test explorer, these 2 code block are working properly, but you don't know what kind of exception is(amount > m_balance or amount < 0), we just know somewhere throw back an exception message.

Revise BankAccount.cs

  • Trace back to BankAccount.Debit and you’ll notice that they used the same exception, so you can determine to use ArgumentOutOfRangeException(String, Object, String) to contain the name of the argument, the argument value, and a user-defined message. ```c# if (amount > m_balance) { throw new ArgumentOutOfRangeException(“amount”); }

if (amount < 0) { throw new ArgumentOutOfRangeException(“amount”); }

1
2
3
4
5
6

## Refactor the code under test
* Define 2 constants for the error message at class scope, put the following code block in the class under test, BankAccout.
```c#
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";
  • Then modify the 2 conditional statements in the Debit method. ```c# if (amount > m_balance) { throw new System.ArgumentOutOfRangeException(“amount”, amount, DebitAmountExceedsBalanceMessage); }

if (amount < 0) { throw new System.ArgumentOutOfRangeException(“amount”, amount, DebitAmountLessThanZeroMessage); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
* Refactor test method like this. Replacing Assert.ThrowsException with using try/catch method to catch unexpected exception situation.
```C#
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (System.ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
    }
}

The last part

  • Not really understatnd about adding Assert.Fail at the end of the test method.

Final Result

Reference