Re: A brief question about testing/best practicesby wee (Scribe)
|on Jan 20, 2015 at 20:54 UTC||Need Help??|
Getting 100% test coverage is sometimes tricky. You can do things like test-driven development (where you design the tests first and then write code that passes them), but sometimes that's not always the best way to go about it.
I've found that things of any marginal importance (and some of seemingly none) tend to grow organically. Someone in another group will ask if your program that does X might also do Y. Or is it possible to have your program's result saved and analyzed later? And then that program becomes a sort of automation tool. That sort of thing. It's not ideal, but I've seen it happen a lot.
So what I do is make as many discrete parts as possible. If the thing is supposed to send an email on completion, I write a function/method that does exactly that, and only that. Then I can write a test for it, and verify that it's sending email. If I later add functionality that attaches a file or whatever, I can run my original test as well as one that tests for an attachment and verify that nothing broke.
Contrast with just writing one monolithic procedural script. It's not very testable, and a change in one place might break a bunch of other things without you knowing about it. But if you can get yourself to the point where you know Method Foo does this thing or that thing, you can write tests that give it one or the other and check to see what it did. And then you write tests that give it neither thing it needs. And then both. And then garbage for one and valid input for the other. And so on.
So when you're writing your initial code ask yourself if stuffing a bunch of junk in a foreach loop in your main program is better or worse than breaking that out into a (testable) subroutine. It may not be. Readability might suffer. You just have to use your best judgment.
For all the "glue parts" that hook everything up, do a search for "white-box testing". It's where you figure out certain inputs into the main program as a whole, in such a way as to exercise specific functionality, and then you check for certain known good output. Using clever tests, you can have your glue code call quite a lot of your non-glue code. Note that you can easily design tests that will never have a real-world analogue. That might or might not be desirable.
You can also do what's called "black-box testing". It's similar to the above, but you don't care about knowing that each possible execution pathway has been tested. You give the program input X and look for a desired outcome. This test sorta pretends to be an end user, in other words. The user does "this" and expects to get "that".
When combined with good unit testing, one or both of those should sufficiently exercise your code as a whole enough such that you have some peace of mind.