Getting the TDD ball rolling

Post questions or comments for this screencast.
This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/getting-the-tdd-ball-rolling

@benorenstein, if you wanted to drive out validations for a book, i.e. a book must have a title. Would you write the feature spec then move down into unit testing?

This seems fine in the case of a simple title-check, but it doesnā€™t feel scalable for large forms where you are testing every permutation of error states for the model in both the feature specs and the unit specs.

Nope, Iā€™d just write a unit test for that directly.

We used to test such things with integration tests, but we found the payoff didnā€™t really offset the costs of the integration tests (slow, less direct to write).

@benorenstein do you do the same thing of writing minimal code to make the code pass while working on real projects? I mean will you wait for the rspec to show errors before you fill in the index action or do you get ahead of the time and write down the index action knowing what it should be doing before writing the test for it? Doesnā€™t this slow you down?

1 Like

@benorenstein I was wondering about ā€œSees a count of the total books in libraryā€ scenario - wouldnā€™t it be better to replace the steps for adding a book with:

Book.create     # or even use factory_girl or something else to not worry about creating valid models
visit root_path  # to reload the page

Testing these 3 steps seems kind of repetitive (and somewhat prone to errors ;)) to me because youā€™re exercising the same behaviour as in the previous scenario. Whatā€™s your take on this?

The more I think about my question the more I feel that feature specs shouldnā€™t know about Book model at all. Iā€™m also watching Intermediate Ruby on Rails part 3 right now where @halogenandtoast does a refactoring and talks how the core appearance of the application hasnā€™t changed while he brakes the code into smaller pieces so it makes sense not to test lower level details like Book.create in feature specs and instead let these ā€œoutsideā€ specs make sure we havenā€™t broken functionality while refactoring. Am I on the right track here?

Yep, absolutely. I work in very small steps.

A test that you havenā€™t seen fail is not a good test. I try to only write the code that the test error is telling me to write. This helps ensure coverage, and helps me get in a good rhythm.

Every once in a while Iā€™ll jump ahead just a bit. For example, if I write a test that says the foo method should return bar, Iā€™ll run the test, watch it fail, and then implement the method, rather than simply defining an empty method foo. But I actually enjoy programming more when Iā€™m being very disciplined about only writing the minimal thing to make the testā€™s error message change (or pass).

Thanks Ben

Hi @benorenstein and thanks for the great video. As always with thoughtbot, itā€™s a awsome resource.

I have a few questions. If I understand, you donā€™t respect the Sandi Metzā€™s rules in this example because you instantiate two objects in the controller. Think than the thoughtbotā€™s philosophy is to not use a more complex pattern if there is no problem. Am I right?

@benorenstein, in one of your talk on vim you said than you use vimā€™s tabs only for completely unrelated things. In this video, do you use tabs just for demo?

This video is definitively the kind of videos Iā€™m looking for. I will be very happy if you continue to develop this kind of application with more complex concept. For example, how and when do you use services or form objects.

Thatā€™s correct. We follow Sandiā€™s rules sometimes, but when it doesnā€™t seem to make sense to us, we ignore them.

Since the focus of this screencast was early TDD, I didnā€™t want to cover presenter objects (which is what I might pass in if I only wanted to pass one thing to my view). However, I might refactor to that later if I felt like we were passing too much to the view, or if I thought there were a missing object that did a good job of encapsulating everything I was passing.

Did I say that? I donā€™t recall that. I mix and match my tabs and windows all the time. When Iā€™m working, I constantly open new tabs and splits and move them around and close them.

Glad to hear it!

1 Like

Did I say that? I donā€™t recall that. I mix and match my tabs and windows all the time. When Iā€™m working, I constantly open new tabs and splits and move them around and close them.

http://www.youtube.com/watch?v=SkdrYWhh-8s. Near 29 minute 10 second. Again, a great talk.

Do you plan to do a serie (build a rails app from scratch) or is it a single video?

I didnā€™t plan on doing a series. Joshā€™s TDD workshop does build up an app over a longer period, so I think that is actually covered fairly well by existing content. What do you think?

@benorenstein Are you coming up with more prime challenges? Maybe this time you could give a rails app as a problem :).

1 Like

Yes! Soon!

For sake of example, letā€™s say we had a very complicated dashboard, displaying a lot of information ( I know this is probably not good interface design ). Would you have an integration test for each one of those parts, or would you instead unit test the dashboard presenter, or both?

Good question!

I think I would write just one integration test for the happy path: visit the dashboard and verify that the important information is present.

Iā€™d drive out the actual methods on the presenter with unit tests.

This way, you get high-level coverage of the happy path (easy), and the edge-cases can be covered by lower-level unit tests (easier than integration testing them).

@benorenstein makes perfect sense.

One more question: Iā€™ve been using page objects in my integration tests and Iā€™m really enjoying them over using ā€˜expect(page)ā€™, but I ran into something last night that I was a little fuzzy about.

Letā€™s say we have a ā€˜ShoutOnPageā€™ for the shouts index action. You can click on a shout and that would take you to the shouts show action.

scenario 'visit shout page' do
  user = create(:user)
  shout = ShoutOnPage.create_shout_for(user)
  visit shouts_path(as: user)

  shout.click

  expect(shout).to be_header_of_page
end

First, is it bad practice to make page objects clickable like ā€˜shout.clickā€™? Furthermore, is it bad practice to be transferring the page object to the show view and asserting something about it there (i.e. in a ā€˜header_of_page?ā€™ method)?

Not sure, as I havenā€™t used Page Objects much. @joshclayton, any thoughts?

@dolphorama to answer your first question, I donā€™t think itā€™s bad practice to make them clickable - youā€™ll just want to be sure each page object is discrete and obvious in how it can be interacted with. A good example there would be a master/detail representation where the object in both the master and detail (when the detail is visible) is clickable.

To answer your second question, the fact that youā€™re bringing this up is great - it exposes what I see to be a breakdown of the page objects and something Iā€™d do differently. Iā€™m definitely not opposed to methods like header_of_page?, but it (at least to me) isnā€™t obvious what thatā€™s testing. I avoid asserting against markup as the name of the method (Iā€™m guessing itā€™s testing that the name is in a header tag) and instead focus on the behavior of the object itself. In this case, because itā€™s a very simple assertion, Iā€™d just check presence (where present? would verify basic information is there on the show page, like title and description).

The other thing Iā€™d do is have two separate page objects - one for the ShoutsOnPage (the index page itself) and a ShoutOnPage. In that situation, youā€™d be able to do something like:

shouts = ShoutsOnPage.new
shout = shouts.create_for(user)
shouts.view # not necessarily necessary, since the create controller action will likely take the user to the index
shouts.first.click
expect(shout).to be_present

This benefits from a single level of abstraction - the page object handles ALL page interaction so thereā€™s no mixture of Capybara methods (like visit) and page object interaction.

I think the best way to learn about page objects is try a handful of different patterns and see what works and what doesnā€™t work. I still feel pretty new to the concept of them and how best to handle these situations so Iā€™ve been trying different things in different projects. The biggest takeaway I can provide is to follow the single level of abstraction - youā€™ll want your page objects to encapsulate all page interaction, so there shouldnā€™t really be any Capybara methods directly written in the scenarios.

Feel free to at-reply me if anything else comes up or if you have any questions, and good luck - page objects are a lot of fun and make writing tests much easier in the long run!

2 Likes