Boris Beizer had defined 5 levels of maturity in testing:
Level 0
There is no difference between testing and debugging. Other than in support of debugging, testing has no purpose. Defects may be stumbled upon but there is no formalized effort to find them.
Level 1
The purpose of testing is to show that software works. This approach, which starts with the premise that the software is (basically) correct, may blind us to discovering defects. Those performing the testing may subconsciously select test cases that shall not fail. They will not create the "diabolical" tests needed to find deeply hidden defects.
Level 2
The purpose of testing is to show that the software doesn't work. This is a very different mindset. It assumes the software doesn't work and challenges the tester to find its defects. With this approach, we will consciously select test cases that evaluate the system in its nooks and crannies, at its boundaries, and near its edges, using diabolically constructed test cases.
Level 3
The purpose of testing is not to prove anything, but to reduce the perceived risk of not working to an acceptance value. While we can prove a system incorrect with only one test case, it is impossible to ever prove it correct. To do so would require us to test every possible valid combination of input data and every possible invalid combination of input data. Our goals are to understand the quality of the software in terms of its defects, to furnish the developers with information about the software's deficiencies, and to provide management with an evaluation of the negative impact on our organization if we shipped this product to customers in its present state.
Level 4
Testing is not an act. It is a mental discipline that results in low-risk software without much testing effort. At this maturity level, we focus on making software more testable from its inception. This includes reviews and inspections of its requirements, design and code. In addition, it means writing code that incorporates facilities the tester can easily use to interrogate it while it is executing. Further, it means writing code that is self-diagnosing, that report errors rather than requiring tester to discover them.
Sourced from Chapter 1, A Practitioner's Guide to Software Test Design by Lee Copeland