2

I have a concern with "honesty" of test when doing TDD. TDD is

  1. Write red test
  2. Write just enough code to make it green
  3. Refactor and let the test green

So far so good. Now here is an example of applying the principle above, such kind of example were already met in tutorial & real life :

I want to check that the current user email is displayed on the default page of my webapp.

  1. Write a red test : "example@user.com" is displayed inside default_page.html
  2. Write just enough code to make it green : hardcode "example@user.com" inside default_page.html
  3. Refactor by implementing get_current_user(), some other code in some others layers etc, letting the test green.

I'm "shocked" by step 2. There is something wrong here : the test is green even if nothing is actually working. There a test smell here, it means that maybe at some point someone could break the production code without breaking the test suite.

What I am missing here ?

bdavidxyz
  • 2,492
  • 1
  • 20
  • 40
  • What makes you think hardcoding the text onto the page qualifies as "Write just enough code to make it green"? Although of course it does have the benefit of testing that the test works. – T.J. Crowder Oct 20 '14 at 07:26
  • 2
    @T.J.Crowder because unfortunately that's the kind of nonsense espoused by famous books on TDD. – Oliver Charlesworth Oct 20 '14 at 07:26
  • 2
    @OliverCharlesworth: Well, as I say, it has the benefit of testing the test, which is a vital thing to do... – T.J. Crowder Oct 20 '14 at 07:27
  • It's true: those three steps don't encompass all of software development. They're only guideposts. Yes, you can game them as described, much as you could game any "3-step process." Just don't do that, though, and you'll be fine. If you're really worried someone else will, you could have the test randomly generate a different email address for each run. – Erin Call Oct 20 '14 at 07:31
  • @T.J.Crowder I don't agree with "testing the test". The test is testing both itself and the stuff under test. – bdavidxyz Oct 20 '14 at 07:32
  • @davidb583 - whether you agree with it or not - imagine you'd hard-coded the address on the page and the test *didn't* go green. You now can spend time working out why the *test* is broken before you go ahead with the real implementation. Whether you choose to do so or not may depend on the rigidness of your own process and your confidence that a "simple test" must, of course, be correct. – Damien_The_Unbeliever Oct 20 '14 at 07:41

3 Answers3

7

Your assertion that "nothing is working" is false. The code functions correctly for the case that the email address is example@user.com. And you do not need that final refactoring. Your next failing test might be to make it fail for the case that the user has a different email address.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 2
    +1 the OP is just in the middle of the process, and needs to add more tests to pin down the exact behavior that they want – Sam Holder Oct 20 '14 at 08:59
6

I would say that what you have is only partially complete. You said:

I want to check that the current user email is displayed on the default page of my webapp.

The test doesn't check the current users email address on the default page, it checks that the fixed email address "example@user.com" is in the page.

To address this you either need to provide more examples (ie have multiple tests with different email addresses) or to randomly generate the email address in the test setup.

So I would say what you have is something like this is pseudo code:

Given current user has email address "example@user.com"
When they visit the default page
The page should contain the email address "example@user.com"

This is the first test you can write in TDD and you can indeed hardcode this to avoid implementing unnecessary stuff. You can now add another test which will force you to implement the correct behavior

Given current user has email address "example2@user.com"
When they visit the default page
The page should contain the email address "example2@user.com"

Now you have to remove the hardcoding as you cannot satisfy both of these tests with a hardcoded solution.So this will force you to get the actual email address from the current user and display this.

Often it makes sense to end up with 3 examples in your tests. These don't need to be 3 separate tests, you can use data driven tests to reuse the same test method with different values. You don't say what test framework you are using, so I can't give a specific example.

This approach is common in TDD and is called triangualtion.

Sam Holder
  • 32,535
  • 13
  • 101
  • 181
  • Yes, but ... A lot of books, including the famous http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530/ref=sr_1_1?s=books&ie=UTF8&qid=1413897717&sr=1-1&keywords=tdd recommends not to over use triangulation. It's usually better (and makes the tests more coherent) to refactor tests with triangulation. Also, randomly generating a user name is not a very good practice in unit testing (anything with the word "random" in it should not really be in unit tests). Still, good answer +1 – ethanfar Oct 21 '14 at 13:24
  • Good point about the triangulation. I'm not sure I agree with the 'nothing random' in a test though. Sometimes you just need a value without needing it to be something specific and having the data generated 'randomly' avoids coding the solution to the test data. If you use the same seed for your random object then you guarantee the same test data every time, despite it being randomly generated. this removes the worries about tests failing randomly because of the test data. [AutoFixture](https://github.com/AutoFixture) is a framework written to provide such data. – Sam Holder Oct 21 '14 at 13:40
0

You are correct about

step 2. There is something wrong here

but it's not in the TDD approach. IMHO it's in the test logic. After all this (step 2) validates that the test harness is working correctly. That the new test does not mistakenly pass without requiring any new code, and that the required feature does not already exist.

What I am missing here ?

This step also should tests the test itself, in the negative: it rules out the possibility that the new test always passes, and therefore is worthless. The new test should also fail for the expected reason. It's vital that this step increases the developer's confidence that it is testing the right thing, and passes only in intended cases.

ekostadinov
  • 6,880
  • 3
  • 29
  • 47