I’m actually preparing a presentation about unit testing in .NET in order to convince one of a team to start implementing it. For some of us, this is an obvious thing that our code should be covered by unit tests but still, there are a big number of projects without them. So, all who don’t enough feel a sense of unit test creation, I encourage you to finish reading this post.

What is unit testing?

In simple language – level of software testing where individual components of a software are tested. What does it mean? Let me show a plain example.

public class BankAccount
{
        private readonly string m_customerName;
        private double m_balance;

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

        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;
        }
}

The above piece of code implements a basic bank account class with a method to withdraw money called void Debit(double amount). It checks if the payout amount is less then account balance and if the payout amount is greater then zero. If so, we are able to withdraw this amount. Now, we want to verify if our method works properly, hence, let’s create a first unit test.

[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");
}

The test checking if the method, for beginning balance = 11,99 and debit amount = 4,55, decreases account balance by 7,44 what is a correct assumption: 11,99 – 4,55 = 7,44. There is only one unit test for this method but of course, we can add others to verify what happens when the debt amount is greater than the account balance or when the debt is less than zero. Try to do it yourself!

Why do we need to unit tests?

  • Quality of code
    Unit tests check cases that you could skip in your implementation. Moreover, attachment of unit tests execution in your CI build or verification of test coverage keep your code in good health.
  • Finds bugs early
    It detects issues that may have come up before code is sent further to the repository or merge to master.
  • Simplify the debugging process
    Run existing unit tests before start implementing a new feature. If a test fails afterward, it means that only the latest changes made in the code need to be debugged.
  • TDD
    Writing tests before actual coding (TDD) makes you think harder about the problem, hence, your code would be more bugs-free then it is.

Popular Unit testing frameworks for .NET

According to RedMonk 2018 report, a framework that gets the most questions on StackOverflow is NUnit, however, XUnit is getting be popular too. Furthermore, keep in mind that at the beginning of .NET life most developers used to implement unit tests using MSTest, so you can also meet unit tests implemented in this framework.

Mocking

Mocking is creating objects that simulate the behavior of real objects. Once again, let me show me a short example to explain what exactly I mean.

public class CreditDecision
{
   private readonly ICreditDecisionService _creditDecisionService;
   public CreditDecision(ICreditDecisionService creditDecisionService)
   {
      _creditDecisionService = creditDecisionService;
   }

   public string MakeCreditDecision(int creditScore)
   {
      if (creditScore < 0)
      {
         throw new ArgumentException("Credit score can not be negative number.", nameof(creditScore));
      }

   return _creditDecisionService.GetCreditDecision(creditScore);
   }
}

public interface ICreditDecisionService
{
     string GetCreditDecision(int creditScore);
}

Above, we have a class responsible for a credit decision containing a method public string MakeCreditDecision(int creditScore) which produces a credit decision in text format. It may be noted the method calls another GetCreditDecision method from the ICreditDecisionService interface. This interface has been injected across a constructor.

Now, we would like to create the unit tests for the MakeCreditDecision method but we need to know what exactly the GetCreditDecision method from ICreditDecisionService does. If it updates some tables from DB or invokes some events we will execute undesirable actions. In order to avoid these actions we just need to add the mocking mechanism. It thanks to mocks we can simulate the behavior of the methods, properties, etc. How to create mock objects using Moq which is the most popular mocking framework in .NET? Look at an example below.

[Test]
public void CreditDecision_MakeCreditDecision_CreditScoreGreaterThenZero_ReturnsCreditDecision()
{
   // Arrange
   mockCreditDecisionService = new Mock<ICreditDecisionService>(MockBehavior.Strict);
   mockCreditDecisionService.Setup(p =>p.GetCreditDecision(It.IsAny<int>())).Returns("Credit Decision");
   var systemUnderTest = new CreditDecision(mockCreditDecisionService.Object);
   var expectResult = "Credit Decision";

   // Act
   var result = systemUnderTest.MakeCreditDecision(543);

   // Assert
   Assert.That(result, Is.EqualTo(expectResult));
}

As we can see the GetCreditDecision method has been mocked. It means for each integer parameter it returns just simple text: “Credit Decision” what it is fully enough for our tests because we don’t want to verify the behavior for the GetCreditDecision method. The aim of the test is checking if the method returns a proper credit decision in a string format for a credit score greater then zero. Additionally, another case that is worth to check is the case for a credit score of less than zero. In this case, we should get ArgumentException and mock object is not required then.

When doing unit tests does not make sense

  • Do not create test cases for everything. Instead, focus on the tests that impact the behavior of the system.
  • The code is trivial e.g setters and getters.
  • If the test itself is an order of magnitude more difficult to write than the code.
  • If the tests run is very expensive like complex calculation or call external resources which causing extra cost.
  • Do not test external libraries.

Summary

On the end, I would like to leave you with a quote from the book “Software Testing Techniques” written by Boris Beizer to draw some conclusions.

More than the act of testing, the act of designing tests is one of the best bug preventers known. The thinking that must be done to create a useful test can discover and eliminate bugs before they are coded – indeed, test-design thinking can discover and eliminate bugs at every stage in the creation of software, from conception to specification, to design, coding and the rest.

About The Author

I am a software developer from Poland, currently focused on the .NET platform. I’ve been incredibly passionate about IT since a young age and am always seeking to expand my skillset. Furthermore, my personal development plan includes machine learning, cryptocurrency, image processing, and the Scrum framework. Turning to the personal part of my life. I’m a licensed paraglider holding an International Pilot Proficiency Information Card, a proud Arsenal F.C. supporter and avid traveller with an infatuation for the natural beauty of New Zealand. I’m keen on unusual or extreme sports, and I love to discover and try out new things.

Related Posts

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.