D.W.,
Background Comment
I got into the software testing industry driven by a desire to answer the questions you posed. Since then, I've been examining ways to (a) create the most powerful sets of software tests that are relatively small in number, and (b) measure the actual effectiveness of such sets of tests. It's a topic I'm passionate about. (I must be passionate or nuts or both as it's currently 5:45 AM as I'm writing this...)
Design of Experiments is a decades-old field devoted to answering your question, e.g., "What should I test if I can't test everything? How can I learn as much as possible in as few tests as possible?" Design of Experiments-based test design methods are widespread in agriculture, marketing, manufacturing and many other industries. These Design of Experiments-based methods to selecting subsets of highly effective tests are absolutely applicable to the software testing field but fewer than 5% of software testers use them to select their tests.
I've been applying techniques from Design of Experiments in the software testing field intensely for 6 or so years now. Many papers and experience reports, including a paper I co-published in IEEE Computer with 3 PhD's, show that these approaches to selecting relatively small numbers of software tests work well. These techniques (most of which are referred to in user246's answer to this question) include:
- Pairwise Testing
- Combinatorial Testing
- Orthogonal Array Testing / OATS
Proposed Solution to Your Specific Question
Assume you have a system under test that has a trillion possible tests that you can think of to execute were time constraints not an issue.
Scenario 1:
You do not have any information about:
- Which code was just changed (e.g., you wanted to test the whole system and it was all built from scratch and yet to be tested),
- Which tests have regularly passed in prior runs (because, in this scenario, by definition, no tests have been run previously)
In this scenario, I would strongly recommend you use a Design of Experiments-based combinatorial approach to selecting your tests. (Disclaimer: I created the test case generating tool Hexawise after measuring the effectiveness of Design of Experiments-based test case selection approaches so I could fairly be accused of being biased).
Start with a pairwise selection of tests, and as you execute those, and learn more abou the system, how it is working, where its weak points are, etc., and other ideas for tests occur to you, then edit those tests by: (a) adding new test inputs and (b) adjusting weightings on your test inputs to focus more on problem areas, and (c) time permitting, increasing the coverage strength of your generated tests to 3-way (all possible combinations of three test inputs would be tested together in at least one test case) or an even higher coverage strength if you're automating test execution.
You will find that this approach to test case selection will be:
- Far more efficient than hand-selected sets of test cases because (a) the Design of Experiments-based tests will be far less accidental repetition of combinations being tested again and again, and (b) the Design of Experiments-based tests will have far fewer gaps in coverage (e.g., 100% of dual-mode faults (AKA pairwise defects)) would be covered.
Scenario 2:
You're testing the same System Under Test (for which you can think of a trillion possible tests you could theoretically execute) but this time you DO have information about:
- Which code was just changed (e.g., you wanted to test the whole system and it was all built from scratch and yet to be tested),
- Which tests have regularly passed and fails in prior runs
In this type of situation, I would encourage you to:
Start with the Design of Experiments-based approach to selecting tests just mentioned, and, as proposed by Alan, also
- Changed Code: pay particular attention to ensuring that you're including tests in your test suite that thoroughly test the recently changed code. You can do this by instructing your test generating tool to seed / include particular tests that you identify in the Design of Experiments-based tests it generates.
- Tests that have failed more often than others in prior runs: consider seeding those tests in your generated set of tests as well.
These approaches will give you:
- Maximum coverage in the fewest possible tests* (both scenarios)
- Minimum wasteful repetition (both scenarios)
- Additional focus on high priority areas (scenario 2 only - e.g., where code was changed and where known trouble spots are
*It is worth pointing out that when you select tests using this approach, you can select a coverage strength to suit your thoroughness objectives and timing constraints. You could generate a couple dozen 2-way tests (AKA pairwise tests or allpairs tests) for example if you were in an extreme hurry or a couple thousand 6-way tests if you wanted extraordinary thoroughness or a couple hundred 4-way tests. Or any number of tests in between.