The Trials of Testing

in #programming7 years ago (edited)

After being on Steemit for almost a week it is high time that I mention my occupation and a little about my background.

I studied computer science in St Andrews, the same university as Prince William (notice the name drop), the oldest in Scotland. Whilst there I learnt many different langauges but predominately concentrated on Java.

St Andrews Swilcan Bridge

Upon graduating I took a role as a web developer for a small advertising company, from there my passion for the web blossomed. Over the years I have created a vast array of different products, contributed to open source projects and worked for agencies and corporations alike. My experiences, my failures (of which there are many), my successes, the collaborations and the interactions have moulded me into a highly opinionated and capable programmer.

I am now a lead backend for a large media organisation, caring for their APIs and contributing to building new exciting products.


Now for the crux of this post. You 'MUST' unit test your code. No cuts, No buts, No coconuts!

Wreck if Ralph

Now I am not suggesting everyone go down the Test Driven Development (TDD) route, I have tried that approach and it wasn't for me. What I am advocating for is that each feature gets a suitable level of code coverage.

Having been in the game for such a long time I have had many encounters with peers and more often superiors who say, "But we do not have the time nor the money to write unit tests."

If I had a Steem Dollar for everytime someone had said that to me, I wouldn't be writing unit tests or even writing this post, I would be on a beach sipping a margarita.

Margarita on beach

I have gotten so fed up with that phrase that my default response is now, "Then you DO NOT have the time nor the money to NOT write unit tests."

If you are a developer, reading this and not writing unit tests, then I implore you to start. Start by adding extra time/points to each of your stories during planning sessions. Start by telling your boss that this is what must be done to meet the minimum requirements before it can be passed to QA. If they disagree or give you shit, send 'em to me, I will back you up. Or even better send them this list.

1. Refactoring


During every project you will write code that needs to mutate, it needs to be changed to facilitate the feature you are creating. Without unit tests how can you be confident you haven't altered the functionality of the original code, introducing bugs? Countless times I have changed one line believing it would not affect anything and countless times my unit tests have failed the build. If these changes had been deployed to a production environment the results would have been catastrophic.

2. Quality


Writing unit tests makes you think harder about the problem you are trying to solve. As a by-product each line will be scrutinised, they will be double and triple checked. This allows you to write cleaner less complex code with fewer bugs. You become at one with the code, essentially you become Neo and who doesn't want to be Neo?

Neo

3. Design Patterns


Most common design practices are there to help: to introduce ideas like the separation of concerns. To make the code base more abstract, more readable, more performant but most importantly (in my opinion at least) more testable.

Consider the example below for instance (most of our API's are written in PHP, don't judge!). The two clients are almost equivalent, but they have one subtle difference, the dependency is injected. We don't really care about the inner workings of Guzzle in our Client classes and we definitely wouldn't want to test it, it has already be done. So why instantiate a new one everytime? It makes the code completely un-testable as you can no longer substitute the dependency for a mock, it has broken the basic principle of the separation of concerns.

<?php

namespace Library;

use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\ClientInterface as GuzzleInterface;

class BadClient {
    private $guzzle;

    public function __construct() {
        $this->guzzle = new Guzzle();
    }

    public function get($url) {
        return $this->guzzle->request('GET', $url);
    }
    ...
}

class GoodClient {
    private $guzzle;

    public function __construct(GuzzleInterface $guzzle) {
        $this->guzzle = $guzzle;
    }

    public function get($url) {
        return $this->guzzle->request('GET', $url);
    }
    ...
}

4. Knowledge sharing


Think of unit testing as a developers documentation. If you have good well thought out tests, with good naming conventions, then it makes it so much easier for future developers to work on the code base. Plus you might not even have to write a tedious doc that no one will ever read. Bonus!

5. Debugging


If your tests passed before the change and they don't pass now, then it is something you have changed. Either fix the tests or fix the code! Simples!

Simples

6. Costs


This is the one every business owner wants to read. This is the big kahuna and the only one that really matters. The above 5 points lead to less complexity, a more robust code base and less technical debt. So just take the hit now and write the damn tests. I promise you that it will save you a lot of heartache and time in the future.

With that being said, the main reason I am writing this piece is to vent some frustrations. We have recently undertaken a large project, to drastically improve the speed in getting it into production we have purchased 3rd party software. When we received this code, I was mortified, there were over 100 hundred thousand lines of code, hundreds of classes, numerous libraries, cyclomatic complexities well in to there 1000s and all of with no tests. NOT 1.

How are we meant to extend this product, alter it to fit our requirements all with confidence that we aren't going to break it. All I can say is I am glad it is written in Python. Monkey patch everything!

Monkey patch everything

P.S. Our base test case class if anyone is interested.

from mock import patch


class BaseTestCase(unittest.TestCase):
    def create_patch(self, name):
        patcher = patch(name)
        patched = patcher.start()
        self.addCleanup(patcher.stop)
        return patched
Sort:  

Another great post. Really like your articles, very informative.

Cheers @widdows9000 just had to vent some of my frustrations, thought this was as good a place as any.

I feel your frustration. For the first half my career I didn't do unit testing and NO place I worked ever asked for it. I started to push for it in my current job several years ago and had a challenge getting management to understand that even though our work estimates were larger initially we would ultimately deliver a better product faster because there would be so many fewer bugs. Every professional programmer should be writing unit tests (and preferably practicing TDD) IMHO.

Thanks,
Rich

Coin Marketplace

STEEM 0.20
TRX 0.14
JST 0.030
BTC 66543.69
ETH 3327.31
USDT 1.00
SBD 2.71