Ensure test coverage when refactoring to mocks

Diposting oleh good reading on Jumat, 07 Agustus 2009

Using real dependencies in your tests will lead to pain. A lot of pain.

Most likely, your test fixtures lack coverage, test the wrong class, be difficult to read and don't do what they say.

Eventually, your team will decide to abandon testing or introduce mocking/stubbing.

Refactoring your existing tests will be a major endeavor.

It will be tedious and repetitive.

You will feel like you are wasting time.

You will have a strong urge to rush.

To help you out, I've outlined the steps needed to refactor a test to mocks. I've noted steps that are easy to skip (yet still important) with *

1. Make a copy of the TestFixture -- call it TestFixtureNameWithMocks -- this will ensure you do not lose test coverage and can commit often.

2. Comment out all tests

3. Delete the setup code

4. Take a moment to read the first test
  • Does this test actually test anything? (if not, figure out what it should be testing and get it working right in the original fixture first!)*
  • Does it verify state of the CUT?
  • Does it verify state of a dependent (or dependent of dependent of dependent...)?
  • Does it do more than one verification?
6. If there is more than one verification, determine how many tests you actually need.

7. If your test relied on the state of a dependent, verify that behavior is tested in the correct test fixture (i.e. the dependent's!). If not, you need one, use this test as an example. Create a failing test now, before you forget. If you have a rabbit hole of dependents, make a test for the top dependent.*

8. If needed, break out the test into multiple verifications -- select one to do, comment the rest.

9. Uncomment test and rewrite using mocks/stub. You will find that some tests are quite different. Some similar. Some will require refactoring of the CUT or dependents to make mock friendly.

10. Run the test and verify it passes. If it does not, figure out what preconditions are missing -- check the CUT and old test. Keep doing this till it passes.

10. Comment out code that is being tested.*

11. Run test, verify it fails for the right reason. If it does not, check the test and verify your assumptions. Make it fail for the right reason!*

12. Uncomment code, verify it passes. If you didn't change code in 11, you can skip this step -- I am paranoid and usually do it anyway :)

13. Repeat for every test in the fixture (and any new tests you find). Pull out common setup if needed. Rename tests since the original names will most likely need it :P

14. Remove old test fixture and rename new one.

If you made it this far imagine how long rewriting one test takes. In the best case you still need to run each test at least 2 times.

Now imagine doing this for more than one class.

How about for how many tests exist by the time our tests are painful enough to require this change.

If done right, this will take a lot of time. If we do it wrong, we take great risks and have tests that give us a false sense of security.

Clearly, if we want to continue delivering end user value, we cannot do this all at once.

Here is one way to do this:
1. Whenever a test is updated (bug fix, enhancement, refactor), update the test fixture to use mocks.
2. Have a policy where it is ok to have 2 fixtures for 1 CUT (one with mocks and one without) to encourage people to start using mocks w/o the overhead/context switch of a big refactor. Stress that finishing the refactor takes precedence over starting new work.
3. Let everyone on the team know that it is expected to take time and include that in commitments and estimations.
4. Remain disciplined

Good luck!!

{ 0 komentar... read them below or add one }

Posting Komentar