Fundamentals of TDD in C#
Tags: csharp, computerscience, testing, beginners
Published: 2021-09-18 09:28:06

If you learned something new feel free to connect with me on linkedin or follow me on dev.to. I've also linked to the GitHub project for those who want to run the code themselves
- https://www.linkedin.com/in/albert-bennett/
- https://github.com/Albert-Bennett/TDDSample

<br/>
**Introduction:**
TDD stands for Test Driven Development. It is a way of developing software where you only write enough code to get your tests passing, and none more. The idea is to fail first and fail often. Developing a project using TDD makes your code more reliable, robust, and helps to prevent bugs.

This is one of those topics that seeing an example really helps out. Well at least it has worked for me. As such, this will be more of a tutorial. For it we will be setting up two C# projects, simple ones. Our use case for our project is to manage billing accounts.

Such that:
- Bills that are null can't  be sent
- Bills can't be sent on weekends

<br/>
**Step 1 - Project Setup:**
This project will consist of a .Net core class library and an Nunit Framework Project. The Nunit project should contain a reference to the class library.

To run any of our test we can click Test> Run> All Tests. And to view our test we can click Test> Windows> Test Explorer. This sample is all done using Visual Studios.

<br/>
**Step 2 - Testing Introduction:**
For this sample I'll be using Nunit. It's my preferred testing framework but, as with everything else software related their are many others. Find one that suits your style or that of the project and use it.

Nunit is an attribute driven framework. As in many of the different ways of defining tests is done through decorating methods with attributes. In the sample you will see the "TestCase" and "Test" attributes being used. 

One other concept to get a round before we start writing tests is the idea of "Assert". Basically it means check if this is X. If it is then mark the test as being passed else fail the test.

A test can have multiple "Assert" in them, and the test will only pass if none of them have failed. 

It brings up the question; should each test only have one Assert clause in them? As multiples can make it trickier to find the issue with the code being tested. Personally, it's fine use how ever many is necessary. There's a debug test option there for a reason.

<br/>
**Step 3 - Our First Tests:**
When writing tests I like to make sure that I write them in a very verbose way. Example "Billing_Controller_Should_Return_False_If_A_Bill_Is_Being_Sent_On_A_Weekend". The reasoning behind this is simple, it's easier to read and explicitly explains what the test is doing. It also helps to make the code more Englishly lokking or easier to read to someone who might not be too familiar with what is being tested in the Test Cases. 

The very first thing we need to do is make sure that:
- false is returned if, the sent bill is null
- true is returned if, the sent bill is not null

This will involve setting up a Bill object and a BillingController and the related tests.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mzgrfurkph8uu6lkzlh2.png)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dmhruji122jdb5mxhiq5.png)

Those first set of tests is to cover ourselves. As in if for some reason, the Bill is null we want the program to fail appropriately (silently). This is so that we might be able to introduce some for of recovery or alternate functionality to the user to get the same result instead of flashing a giant red box on the screen.

Cool. Now our condition is that Bills can't be sent on weekends. So lets write a test for that, this will mean that we have to modify the Bill object to include the date that it was sent.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ld8p0vmk5dyp19cnr93c.png)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/txy0c5d7lv7pw2dniv7i.png)

Notice how this new test has defined TestCase attributes. These are used to test the test... with various different data. Using TestCases can be a great time saver, I try to use them whenever I get the opportunity to within reason of course. There is no point testing something out of the domain of the test after all. 


And with that you know the basics of TDD. I find that the hardest part about TDD, is finding all of the test cases. Another benefit of TDD over regular functional debugging, is that it can take less time to test the application as you are writing the tests as you are going along. 
With all that being said TDD is most certainly a mindset more than anything else. It's an approach the entire team have to stick to, of course there are ways to fudge or get around using TDD. If TDD is something that you are looking introduce to a team I've found that building some kind of code coverage mechanism (into your CI pipeline for example) such as sonarqube to check for code coverage (how much of the code has been tested) can really help the team to stick to TDD.


That's all folks, thanks for reading!