<p>Let's say we have a simple function defined in a pseudo language.</p>

<pre><code>List&lt;Numbers&gt; SortNumbers(List&lt;Numbers&gt; unsorted, bool ascending);
</code></pre>

<p>We pass in an unsorted list of numbers and a boolean specifying ascending or descending sort order. In return, we get a sorted list of numbers.</p>

<p>In my experience, some people are better at capturing boundary conditions than others. The question is, "How do you know when you are 'done' capturing test cases"?</p>

<p>We can start listing cases now and some clever person will undoubtedly think of 'one more' case that isn't covered by any of the previous.</p>

Don't waste too much time trying to think of
every
boundry condition.  Your tests won't be able to catch
every
bug first time around.  The idea is to have tests that are
pretty good
, and then each time a bug
does
surface, write a new test specifically for that bug so that you never hear from it again.
Another note I want to make about code coverage tools.  In a language like C# or Java where your have many get/set and similar methods, you should
not
be shooting for 100% coverage.  That means you are wasting too much time writing tests for trivial code.  You
only
want 100% coverage on your complex business logic.  If your full codebase is closer to 70-80% coverage, you are doing a good job.  If your code coverage tool allows multiple coverage metrics, the best one is 'block coverage' which measures coverage of 'basic blocks'.  Other types are class and method coverage (which don't give you as much information) and line coverage (which is too fine grain).