Jump to content

  • Log In with Google      Sign In   
  • Create Account





Test-driven: Or, how I saw the light

Posted by evolutional, 03 April 2013 · 954 views

testing .net c# development tdd test-driven climbdown
T minus 1 month.

In my current project I'm working with a rare-breed programmer. An SDET, a Software Development Engineer in Test. These guys are accomplished programmers but focus on software quality.

"Hey, I write quality software", I say, "It works, doesn't it?".


T minus 4 years.

Working in a software team for a financial shop. The Head of Software keeps talking about Agile software development, Test-driven development, Continuous Integration etc. Sounds like a plan. Try it. Nice idea, can't see how it works. "I always test my code". I say. I do. Console application which wraps the component, fire in data, breakpoint it, verify outputs. Sorted. Job done.


T minus 3 years 11 months.

Cajoled into writing "Unit Tests". Great, double the amount of code to write, same amount of time = less functionality. Features creep, pressures mount, tests get disabled. Code rot. "I write quality software, it works doesn't it?".


T minus 3 years 9 months.

Epic megafuckton of a bomb drops. Trying to debug, fix, unravel. Breakpoints are my friend. "it works on my machine". Late nights. Slipped deadlines. Break original functionality. QA test team get blame. "Test should have spotted this, it's a basic bloody error!".


T minus 6 weeks.

A leopard doesn't need to change its spots. "I've been coding in OOP systems for years, I know the best practices!"


T minus 7 years.

Massive refactor of game engine. Deeply nested inheritance trees. Ugly! Decide to favour composition.


T minus 5 years.

Massive refactor of game engine. Too many hidden dependencies. This OO-lark is a pain!


T minus 3 weeks.

"We need to ensure high code quality on this, try looking at test driven development or something".

"No, that doesn't work. Tried it, takes too much time and gets in the way".


T minus 2 weeks.

Dependency Injection. Heard of it. Tried it in the past, found it to be useful in some situations. I'll learn that later. No rush.


T minus 7 days.

"This code you've written needs a real Cloud Service deployment!".

"Use dev fabric, it's what it's there for! I've written a load of tests to verify it works!"

"But you still need a hard external dependency!"

"That's life".


T minus 4 days.

Trying to test code which relies on something I have no control over. "I know this can happen, but I have no idea when or what causes it. It's part of Azure, how do I sort this?".


T minus 3 days.

"I need to learn this stuff". Spends long weekend reading, on YouTube and more, practicing, practicing, practicing.



T

The way I write code now is fundamentally different to how I wrote code 7 years ago, 7 months ago, even 7 days ago. I always prided myself on being a half decent software engineer, but ultimately I will confess - the majority of the code I've ever written in my lifetime has been very hard to isolate and test. I've followed best practices as much as possible, but have almost always ended up with hidden dependencies, internal state that's hard to affect or even worse, hard external dependencies.

Having embraced concepts like Dependency Injection, Continuous Integration, Mocking/Faking and Test-driven Development I finally feel like I've added security and confidence around the things I code.

I've tried all this in the past, even blogged about how I didn't get on with it. It's a tough sell. It adds time to development, perhaps even doubles it, but it's worth it.

Using DI and mocks, I can abstract away hard dependencies on external services or libraries and replace them with fakes. I can cause those obscure exceptions and make sure my code handles it. I don't have to wait for a blue moon, I don't have to slay 15 virgin princesses and hope that quantum mechanics favours my outcome today. I can fake the whole thing and force it. I have full control over the environment my code works in. It's like being a god of bits.

What's more, it has fundamentally changed the way I code. It sounds odd, but everything I code now I see the coupling, I see how state migrates between related components. I've done this stuff before, after all it's "good engineering practice", but lets face it - I must have gotten it wrong. Doesn't matter if the odd one slips in there does it?

I realise that to many of you this may be a "well duh". But to me it's a huge swing. A swing in attitude, mindset, approach and style. it's a paradigm shift that required a "click" to happen. I knew all this stuff before, but now I live it. I find it hard to see how I can go back to my old ways.




It's posts like these that make me wish I'd 1) Completed more university education and 2) Worked where I might have access to experienced and quality mentors. Being so comprehensively self-taught, I am well aware of exactly how weak my fu really is. I'm mostly a cowboy in the worst, most ungodly American nuance of the word, and it really shows sometimes. Dependency injection, continuous integration, test-driven... these are all phrases I know, and kind of understand at least on some level, but so often I just look at them as the sort of thing I don't really need or have time to learn, or as something completely irrelevant to my own processes, and I'm aware that the quality of my code has suffered because of it.

Agreed with JTippetts, and it would answer questions like "is there anything ORM produces that we don't do already with our hand-written database code?" As in, our clients don't ask for it so it never gets called into question in meetings. I guess cowboy code is usually the stuff of start-ups started by business majors (without a programming education).

I started test-driven development not long ago, when I changed languages and started rewriting all of my code. I've never been able to comprehend why I would spend all of this time writing tests that I hate for functionality that works just fine, but heres the thing: I will always hate writing tests, and the functionality doesn't always work just fine. Every now and again, serious bugs slip by due to testing only enough to ensure that it is functional, then adding it to the final product.

 

There were some bugs that I found while rewriting old code that I might never have found if I had gone with the usual "run it until it breaks; if it doesn't break, it's bug-free" method. TDD didn't inherently give me speed of development; the hundreds of tests for a module gave me both a supreme peace of mind, and an instant notification that something was wrong. Though it took me twice as long to write it, I saved more than that in maintenance and bug hunting. Additionally, it helped quell feature-creep in some cases. In order to write tests for all of the functionality before you write it, you have to have at least a little bit of a plan of what functionality you will write. This makes it harder to get half-way through a module, and decide you want to add one more feature to it.

Now, by far, the most expensive use of my time is my new resolution to write documentation for every module as I code it. My headers with documentation are far larger than their implementation source file size, and I spent far more time with that than TDD.

Good post for me to read. I see myself in your T - x earlier posts in terms of my current attitude but the programmer I work with is pushing for more TDD approach and I know I need to get over the "twice as much code for no gain" attitude I have. Our particular industry means we can be costing clients a lot of money if we cock up.

 

Good to read someone else's experiences of the attitude shift. I need to grow up and get on this stuff.

PARTNERS