Friday, February 13, 2015

Unit Test Coverage: What Parts of Your Application Do Your Users *Not* Care About?

Developer events are a great place to talk to tons of folks and get different viewpoints. This past weekend at the South Florida Code Camp was no different. I had a chance to spend some time talking with my friend Barry Stahl (blog & Twitter) about unit testing, and he's got some great perspective.

My Unit Testing History
I'm a big proponent of unit testing. I've seen the advantages in my own work. With good tests in place, I code faster, I code more confidently, and I'm more productive. But I've also had an interesting path to get where I am today.

2-1/2 years ago, I first wrote about my journey into unit testing. I can't say that I'm proud of the path that I took, but eventually I ended up where I needed to be. Since then, I've been exploring different techniques to find things that work for me. This included looking at TDD and going through Kent Beck's book Test-Driven Development by Example.

I'm still not completely in the TDD world. I do use it from time to time. I like to experiment with it (like with TDD & Conway's Game of Life). But I still have a bit of disconnect of how to apply TDD in certain situations, for example when I need to get data out of an unfamiliar service.

So, right now I'm more of a "test along side" kind of person. I interleave writing code and unit tests, but usually the code gets written first.

Code Coverage
So back to Barry. Barry and I ended up talking about TDD and unit testing in general. So I brought up how there are certain things that I'm not sure how to test first and also how I don't necessarily agree that code needs 100% test coverage. (This is probably a knee-jerk reaction to some folks I worked with who *only* cared about having 100% coverage regardless of whether the tests were any good or not.)

And that's when Barry said,
What parts of your application do your users *not* care about?
That's pretty hard to argue with. If something is important to our users, then we should have a test that exercises that code. The tests are the proof that the code actually does what we think it does.

(And if there are parts of the application the users don't care about, then those parts probably should be removed altogether.)

Our discussion went a bit further on the parts that we unit test vs. manual test vs. something in between: UI elements/layout, business logic, presentation code, etc. We need to make sure we're using the best tool for the job.

But this isn't the only thing that Barry gave me a different perspective on.

Do I Need a Repository?
I use data repositories in a lot of my demo code -- primarily because it's an easy concept to grasp. This makes it an approachable way to show different abstraction techniques (whether interfaces, dependency injection, or something else). And I've already looked at the question of Do I Really Need a Repository?

One of the primary reasons I have a tendency to say "probably not" is because I don't like to add an abstraction that is not being used. This adds complexity to our code, and we really shouldn't do that unless we're getting some real benefit back.

The YAGNI principle guides us to not code based on speculation. So if someone asks if they should add a repository interface because they *might* want to switch from Microsoft SQL Server to Oracle (or some other data store) at some point in the future, I would tell them *not* to add the abstraction yet.

The truth is that we rarely need a 2nd data store in our application. At least that's what I thought. Barry's viewpoint was that we almost always need a 2nd data store:
The 2nd data store is the fake or mock object that we use in our unit tests.
Again, this is pretty hard to argue with. In order to adequately test our business logic and presentation code, we need to de-couple it from the concrete data store. We don't want a dependency in SQL Server to cause problems in a test of our business logic. (And in my presentations on interfaces and dependency injection, I specifically show how they can make unit testing much, much easier.)

So this has made me rethink things a little bit. I'm still reluctant to add complexity to my code for the sole purpose of unit testing. But we also need to strike a balance at some point. If my tests become overly complex (because I have to use reflection to break dependencies), then it's likely that I won't maintain tests or have adequate coverage. There is a balance point where we have code that is easy to maintain and also easy to test.

Wrap Up
I love talking to other developers. Barry is one of my friends who I met in Phoenix (where he lives). Since then we've talked at lots of other events including in Southern California (where I live). What's interesting about our last meeting is that we were both in South Florida -- several thousand miles from home for both of us.

As we interact with other developers, we can learn from their experiences. Barry is big on TDD, so I'll have to tap into him for a pair programming session to go through some of the areas that I struggle with.

I met a lot of great developers at the South Florida Code Camp -- mostly new people since I was far from home. But I saw a few familiar faces as well, like Jim Wooley. I really wanted to go to both of Jim's sessions -- one on Roslyn and one on Reactive Extensions -- but I was scheduled to present during the same time slots. (That happens a lot as a speaker.) But we had a chance to talk again toward the end of the day, and I got the short version of his Roslyn talk.

As we get different perspectives, we expand on the way we think about things. I find myself getting nudged in new directions, and I look forward to using them to become a more effective developer.

Happy Coding!

No comments:

Post a Comment