← Back to Upcase

Getting the TDD ball rolling


(Upcase ) #1
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

(Noah Hendrix) #2

@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.


(Ben Orenstein) #3

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).


(Charlieanna) #4

@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?


(Uģis Ozols) #5

@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?


(Uģis Ozols) #6

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?


(Ben Orenstein) #7

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).


(Charlieanna) #8

Thanks Ben


(Guirec Corbel) #9

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.


(Ben Orenstein) #10

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!


(Guirec Corbel) #11

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.


(Guirec Corbel) #12

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


(Ben Orenstein) #13

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?


(Charlieanna) #14

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


(Ben Orenstein) #15

Yes! Soon!


(Dolph Mullen) #16

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?


(Ben Orenstein) #17

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).


(Dolph Mullen) #18

@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)?


(Ben Orenstein) #19

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


(Josh Clayton) #20

@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!