Here are some classic dynamic analysis problems: Testing: * creating tests * test prioritization and selection * test quality Debugging: * reproduce * localize * fix As a digression, here are terms you should use instead of the ambiguous, informal term "bug" (the definitions are adapted from http://en.wikipedia.org/wiki/Dependability). Defect:: A flaw, failing, or imperfection in a system, such as an incorrect design, algorithm, or implementation. Also known as a fault or bug. Typically caused by a human mistake. Error:: An error is a discrepancy between the intended behavior of a system and its actual behavior inside the system boundary, such as an incorrect program state. Not detectable without assert statements and the like. Failure:: A failure is an instance in time when a system displays behavior that is contrary to its specification. A test case consists of: test input test oracle Don't forget either part. We have discussed that a static analysis, akin to a proof, can provide guarantees, but a dynamic analysis cannot. That's not entirely true, because a dynamic analysis can give a guarantee; for example, testing can verify correctness. You just have to try try every input. In practice, this is impractical because it is too inefficient (it might even be infinitely large), but it should be what you have in the back of your mind every time you write a test suite. It's no worse (in terms of computability) than performing a perfectly precise static analysis that never performs abstraction or throws away information. Both static and dynamic analysis are equally about deciding what information to abstract, which is an engineering tradeoff. When you write a test suite, think of writing the exhaustive version, but then remove tests that are redundant. An alternate way of thinking, that is closer to what you will actually do, is to add tests so long as they are not redundant with existing tests. Sometimes you can prove that two tests are redundant, but usually the redundancies are based on heuristics. What tests are redundant? * partition the input * use abstractions as you might for static analysis * toy example: even vs. odd * corner cases: 0, null, empty list, empty file * co-occurrence of failures * in the past, 2 tests always succeeded together or always failed together; one seems redundant with the other * environmental assumptions * inputs * outputs * libraries * input size: every input up to a given size * small scope hypothesis * internal metrics * program state, data structures * call sequence or trace * coverage: if 2 tests achieve the same coverage, they are redundant with one another * code coverage ("structural coverage") * line * branch * path * specification * think of it as pseudocode and cover it * values (like corner cases and like partitioning of input or program state) Model checking is a fancy technical term for brute-force exploration of all inputs (up to some bounded size or some other limits). It's challenging to achieve this efficiently. Test suite quality: how good is your test suite? * the only thing that actually matters: real defects exposed as failures * what everyone measures: coverage * it's reasonable to believe there is a correlation (that is, coverage is a proxy for effectiveness in exposing defects), but the correlation is not perfect * another thing researchers measure: fake bugs exposed ("mutation score" or "mutation coverage")