Unit Testing — Pros and Cons — Understanding why testing code is a good practice

Ben Watts
7 min readFeb 29, 2024

--

What is unit testing? Well a unit test should aim to test a specific feature in our code, be it a function which calculates a value, or a value that is gathered from some source. The key word is “unit”, it should test one thing.

This blog post we will take a look at the pros and cons of unit testing, and yes there are cons to unit testing. I did a previous post about unit testing, but it was more of a coding example. If you would like to take a look at that post, just follow this link — https://medium.com/@canvascodebw/unit-testing-why-write-code-to-test-my-code-seems-a-little-redundant-c-example-25a0d0202c5f

This post I’m going to focus more on the advantages and disadvantages of writing unit tests. This post is also going to ignore TDD (Test Driven Development), I’m choosing to ignore this as TDD isn’t something developers start with, this is a discipline which you may adopt later in your career.

So a few years ago, I was creating some game logic in C#, I had many classes which all spoke to each other to work out damage, attributes, monsters etc, I had unit tests for all of my code. Now with this code I had no game platform, just code. I built my game logic into a DLL and referenced it in a Unity project. After I wired up my game logic to be invoked by Unity, something magical happened. I will remember the feeling as it was pure magic. The game just worked, damge, attributes, monster etc. All of the game logic was created without a “game” per se and I tested it all with unit tests.

Next time you have a personal project or time, try creating the logic code in a library with unit tests, then once it is all done, wire it into your desired architecture. I hope you experience the same magical moment as myself.

Right, anyways, back to the post. Yes, testing your our code with unit tests is always a good idea, it provides evidence your code works and security on future changes, but there are disadvantages… Firstly let’s go over the benefits you get from testing your code.

Detecting Bugs — By writing unit tests to test specific parts of our application, we can provide whatever parameters we like and confirm the output is correct. Someone said to me “The ClientId value will never be null in production”, however in the code the ClientId was marked as nullable, after they wrote some tests with this value as null, they noticed the logic was wrong. Don’t work off of assumptions.

Overall Cost — Maintaining and improving code can be very costly, especially when something hasn’t been touched in a good while. If you don’t have unit tests you have no proof the changes haven’t broken anything, also knowing what the system does could be hard to understand without tests to prove you are on the right track. Testing manually will increase the time spent, if we had unit tests to prove the latest changes we did haven’t broken anything, we can then test those specific parts in the test environment instead of the entire application.

Confidence in changes allows for a smoother release process — Unit tests give us the confidence that any new changes to the system haven’t broken any pre-existing features. This provides evidence to the business that with the latest release, all existing features will work without a problem. This is also a massive load off when a new developer comes into a new project and has to do a change. The unit tests will back them up and prove they haven’t broken any original features.

Code Refactoring — Upgrading a system to use a new library or to just remove redundant code, this happens to applications at some point in its lifespan. Having unit tests can once again prove to us that the latest library still works as intended or even that huge refactor from using a custom object to a simple Array hasn’t broken anything.

Debugging at a click of a finger — When a bug is experienced in an environment, the first thing most developers will do is update their application to use the same configuration, run the application and then do whatever they have to, to replicate the steps (this can be super long and very repetitive). Having tests written, we can find the specific method which is causing the problem and replicate the parameters. To run a unit test is as simple as right click > run test, this will always be faster than running the entire application.

Cleaner Code — Code that has been designed with testing in mind will be cleaner, to make code testable can sometimes be a struggle, but as you resolve these struggles you will learn how to avoid them in the future. This in turn will make you think more about the design of an interface, class, function etc. You will start to see what should be provided as a parameter and what may need to become an interface to get around a specific part of the system. Your start to notice how annoying the “new” keyword is.

Documentation — Well this is a tricky one, first off I’m sure most of you will imagine documentation as a document of some sorts which states the requirements, flows, architecture etc. But documentation is the code, code doesn’t go out of date (at an application level), a document will go out of date within a day, business requirements never stop changing. Unit tests provide us documentation of our application, the tests themselves will state features like so “WhenAUserLogsInForTheFirstTime_DisplayAWelcomeMessage” or “WhenClientPurchasingXAmountOfItems_SendThemAVoucher”. I don’t even need to tell you what the application is, you can probably guess from the unit test method names. Our tests define to all developers what this application does and doesn’t do.

So what are these disadvantages? I mean with those excellent advantages what could be the disadvantages? Well let’s go over some below.

Fake Confidence — Unit tests give us confidence that our code works under all provided circumstances… “Provided” circumstances. What if we didn’t think of every circumstance? Did we check a specific if statement is hit or if we provide an invalid parameter? This can happen and will happen, at some point a specific path won’t be tested, why? Because you’re human and something will slip through the gap at some point. I’ve had this happen to me several times.

Discipline — All new code or refactored code should be unit tested, this sounds quite simple, but many developers forget or choose not to write unit tests, due to the setup either being “impossible” or the code is hard to understand and they aren’t sure what needs to be setup. I will repeat this, all new code or refactored code should be unit tested, this helps yourself and others in the future to understand the feature and confirm changes haven’t broken old features. Also, you want to make sure the changes you did actually worked yeah?

Regular Reviewing — If the project’s unit tests aren’t part of the build/deployment pipeline (build/deployment should fail if a single unit test fails), they lose most of their benefits, yes a developer can use the unit tests to understand the code and debug, but if something isn’t telling you about failed tests or if the development team aren’t educated to write unit tests for the code they write/modify, the existing unit tests will become out dated and will fall into the Fake Confidence section.

Experience — Writing unit tests is one thing, but also creating your code to be testable in the first place can be quite a big learning curve, especially for new developers. Learning to write effective unit tests will take time and practice. Learning how to set up a specific function to return the correct outcome when it has dependencies can be another problem. Learning about Stubs, Mocks, Spies and Dummies to manipulate your state/dependencies can take time. Testing is a whole skill in itself.

Overall unit testing is a massive benefit. Looking at the advantages above you gain a huge amount. Detecting early bugs, Confidence in changes and releasing, Debugging at a single click, Documentation etc. Don’t forget my entire game logic that was wrote outside of Unity, just slipping into Unity without a single problem.

Now the disadvantages I provided can all be overcome. To overcome most of them you just have to start writing unit tests for all the code you write from now on. This will help with the experience and discipline disadvantages. For the regular reviewing, your build/release pipeline should invoke your unit tests and fail if a single unit test fails. This will stop a broken build from being published, enforce developers to write passing tests and to fix any tests if they are broken. Code Coverage is also a great tool to enforce a percentage of the code base is tested.

Fake Confidence, now this one will forever remain, no matter what you do, if you write your code and then write unit tests afterwards, this can still be an issue. Of course you can create X amount of unit tests to test everything and you could remove this disadvantage, but the best way to remove this is to follow TDD. Test Driven Development is a discipline, with TDD the basis is you write your test before any logic code is written. I’m not going to go into TDD, but I will put a reference at the bottom for you to peruse.

Thank you for reading, I hope this has helped you or has taught you something new. Feel free to provide feedback, positive or negative.

TDD — https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html

Image — https://www.pexels.com/photo/mechanical-computer-keyboard-671629/

--

--

No responses yet