Code coverage is an common metric to measure code quality but it doesn't guarantee that tests are really testing the expected behavior.
Mutation testing can be described as tests of tests. It consists of introducing mutations to your codebase and checking if at least one test fails for each mutation (we say that mutation is killed in this case, otherwise it lived):
- inversion of conditions
- change boundary conditions (< becomes <=)
- change incrementation of counters
- change mathematical operators
- return null instead of returned value
- don't call void methoids, constructors...
- many other type of transformations
If a mutation is killed, that means that your tests are covering some edge cases, giving you more confidence about the quality of your tests.
Mutation testing can be an heavy process, so it doesn't need to be executed as often as unit tests, but it can be a step in your release process.
Java projects can use Pitest library.