5

According to this answer to this question, a good reason for always starting with a failing test ("Red") is to make sure that the test is working and that the code that will be written is what makes the test passes. (my paraphrase)

Since it appears to me that the ability to write a test before any code has been written is a kind of mental acumen that not everyone can do (I can't), would it be sufficient if I write the code first, then write the test, then revert my code change to see that the test will fail without the code change?

Suppose that could be automated by some kind of IDE integration - would that be an alternate form of TDD?

In development environments in which TDD is enforced by the IDE/Framework, does strict mechanical rule enforcement limits developer's options for optimizations?

An example in which this might be useful:

Suppose the module and its unit tests have already been completed and are both working correctly. Now, I want to introduce a refactoring which would improve the performance by an unknown amount by reducing the number of internal function calls, without any fundamental change in the external behavior of the module.

Because I don't know how much benefits I would get from that refactoring, I can't write a test for that. But without a failing test, I'm not allowed to modify the code. This seems a chicken-and-egg problem.

If I am allowed to write a dummy test which simply captures the number of internal function calls in the old/new code, and then modify the code (in the name of "refactoring"), now I can update my test (if the refactoring is successful in reducing the number of internal calls) to assert that the number of internal calls should be reduced.

(Afterthought)

Although, this type of performance tests has a lesser importance than requirement unit tests:

  • I would like to keep the benefit of the more efficient code if there is no change in functional requirement, and I do not want others to "refactor away" this benefit based on the assumption that no functional requirement would be broken.
  • However, if there is a change in functional requirement, and if it's not possible to meet the current performance level, then the functional requirement should be met first. After that, a new performance target would be set from experimentation.

(Disclaimer: I have never done strict-TDD (and am skeptical of it), although I have been doing test-soon-after-coding for several months.)

More afterthoughts

  • The code change in this question appear to be "post-TDD development", since it occurs well after all functional requirements had been met and all tests were in place. Therefore, as long as all of the original TDD unit tests continue to pass, this type of code change is considered "tuning".
1
  • As the author of the answer you're paraphrasing: Your paraphrase is fine. No worries. May 8, 2011 at 15:42

5 Answers 5

4

If the question is "Can I do TDD without writing the test first" then the answer is no. Even if you write code, test, and revert, you aren't learning how to TDD or getting the benefits of driving your development with tests. I suggest taking the time to acquire the skill of writing the test first. It will be worth it.

You can write the code first, you just won't be doing TDD.

To your example, if you have tests that cover the behavior, you can refactor. TDD is about behavior, not implementation (which is why I prefer BDD). Changing the performance is not changing the behavior, it is changing the implementation. This is exactly the type of thing that proper testing facilitates.

As to your afterthought: comments are appropriate when the code itself does not and cannot be made to communicate some intent. In this example, the code has been written for performance and it may be important to let future developers know that a certain algorithm has certain complexity requirements.

1
  • One of the best comments in code I ever saw was just a reference to a journal paper that described exactly what was going on. Figuring that sort of thing from just reading the code would have required a level of genius that's incredibly rare… May 8, 2011 at 22:22
7

A couple of things you're saying here don't quite match standard XP practice or semantics.

without a failing test, I'm not allowed to modify the code.

Not so. Without a failing test, you're not allowed to add functionality. Refactoring specifically includes in its definition (as we use it in XP) not adding functionality; you may refactor without adding tests.

I want to introduce a refactoring which would improve the performance by an unknown amount by reducing the number of internal function calls, without any fundamental change in the external behavior of the module.

If you've done TDD, the external behavior of your system is already well test-covered. You can go ahead and change things as long as you keep those tests passing. Typically, we would not expect to see significant performance gains from a pure refactoring, but would refactor in such a way as to facilitate performance optimizations. Those optimizations are (again, typically) not refactorings - they change system behavior - and therefore would in a perfect XP environment be the subjects of tests, written first.

It's possible that a reorganizational refactoring such as you describe might affect performance; if you think that it will (and most especially if you are doing it in the hopes that it will), you should instrument the code and compare performance before and after the refactoring.

As far as your main question: I think you are resisting TDD too hard; I think you would benefit from pairing with someone who is comfortable with the practice, and ultimately I think it would help you to get comfortable with the practice so that you can choose to use it or not from a better-informed position. If you refuse to TDD, the practice you propose might be helpful, but it is not TDD and you are unlikely to accrue many of the design benefits of TDD with your practice.

3
  • Suppose we call those code changes "rewrites" instead of "refactorings" because system behavior might be changed. How does one write tests for those performance changes before the exact impact of those changes are known? Or, should one not write performance tests of those nature, since they do not represent a true must-have requirement, but merely a nice-to-have characteristic?
    – rwong
    May 8, 2011 at 16:04
  • 3
    Performance is not a behavior. It is an implementation detail. May 8, 2011 at 17:02
  • @Rein: +1 despite the fact that I don't entirely agree with you. Sometimes it is critical to achieve a certain level of performance because of real world constraints. Also, some optimizations (e.g., choosing a better sorting algorithm) can be functionally identical but have substantial performance implications; functional behavior cannot be the whole story (but measuring non-functional characteristics can be hard!) May 8, 2011 at 22:18
5

TDD is about writing the API before the implementation. By writing the tests you design the API from the "Outside" instead of doing it as a "hmm, how can I call the code I've written". You need to do it that way in order to be able to test the results.

I am not fully convinced that you are absolutely unable to write simple tests describing what the code you need will do. And the first test WILL fail because there is no code behind it doing what you expect. You adding the meat will change that.

Please reconsider your situation and try to have another go.

1

What you describe sounds like a Characterization Test. A good recipe for writing Characterization Tests includes the following steps:

  • Use a piece of code in a test harness
  • Write an assertion you know will fail
  • Let the failure tell you what the behavior is
  • Change the test so that it expects the behavior that the code produces
  • Repeat

However, this has nothing to do with TDD, since the code is not driven by tests.

2
  • So, should all non-requirement tests (such as performance assertions) be separated from the unit test project and placed into a separate characterization test project? (After all, they are different in nature)
    – rwong
    May 8, 2011 at 16:01
  • 1
    Characterization tests are a sub-category of unit tests, and unit tests must have deterministic outcomes. Performances tests don't fall into that category at all... any test that measures performance is a completely different thing than a unit test, so it would never make sense to bundle those together. May 8, 2011 at 16:04
1

You say that you don't have the "mental acumen" to write tests first. I think you do. If you can't write the test first, its a sign that something else is wrong.

In the case of your performance example, the problem is that you are testing something that shouldn't be tested. You should only test that your code produces the correct answers not how fast it produces those answers.

The primary benefit of writing tests first is not to be sure that the test fails before the change was made. Rather, the benefit is that it forces you think of your class from the perspective of the user rather then the implementation. Essentially, it forces you to design the interface first.

Having said that, I do sometimes write parts of the implementation first. An example would be when writing a function and I'm not clear upfront what inputs the function will require. In that case I'll write a first implementation of function before the test. But then I switch back to test-first for the rest of the implementation of the unit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.