tag:blogger.com,1999:blog-12467669.post8566447279916520083..comments2023-04-29T07:23:25.825-04:00Comments on Jay Fields' Thoughts: Testing: One expectation per testJayhttp://www.blogger.com/profile/14491442812573747680noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-12467669.post-64442726731767988342008-09-18T21:52:00.000-04:002008-09-18T21:52:00.000-04:00Very nice post, Jay. How would you suggest writing...Very nice post, Jay. How would you suggest writing tests for a method that throws an exception? Given certain input conditions, this method is expected to throw an exception, and I want to test for that. But there are other expectations to test for as well (such as confirming that the method closes a connection it has opened), and in order to separate these out, it seems like one would have to catch and discard the exception in all the other tests.<BR/><BR/>This seems like it would introduce a lot of clutter and introduce an irrelevant implementation detail to the other tests. (They don't care whether an exception is thrown; they should ideally be able to testing their piece in relative isolation.)Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-29623466710653774472008-02-09T14:43:00.000-05:002008-02-09T14:43:00.000-05:00Pat,In this case, I don't think there is a way I'd...Pat,<BR/>In this case, I don't think there is a way I'd improve the test or implementation. Unfortunately, sometimes you cannot avoid having to add things such as MaidService.stubs(:notify) in several tests. It's not ideal, but it sucks the least in my opinion.<BR/><BR/>Of course, the domain could be modeled in another way which might help, but that's outside the scope of the example.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-79560616920389637702008-01-30T12:40:00.000-05:002008-01-30T12:40:00.000-05:00Jay: So how would you change the test/implementati...Jay: So how would you change the test/implementation to avoid breaking tests when you add a new test? The code looks pretty good to me.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-47274028952647877192008-01-30T10:48:00.000-05:002008-01-30T10:48:00.000-05:00@Jay Yes, that works, I should have tried it first...@Jay Yes, that works, I should have tried it first. Thanks. :-) It's all there in spec/spec/matchers/description_generation_spec.rbAna Nelsonhttps://www.blogger.com/profile/07952731641901031606noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-33140737304094476832008-01-30T10:05:00.000-05:002008-01-30T10:05:00.000-05:00@Pat, I see breaking a previous test as a good thi...@Pat, I see breaking a previous test as a good thing. Ideally, changing the implementation would not break a previously written test. When adding new implementation does break a test it signals to me that either the test or the implementation can be written in a better way.<BR/><BR/>@Ana, I believe you could write the test as: it {@x.should be_nil}<BR/><BR/>Of course, I can't help but mention that I'd also be looking to <A HREF="http://blog.jayfields.com/2007/06/testing-inline-setup.html" REL="nofollow">Inline Setup</A>Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-16019849530678368962008-01-30T09:28:00.000-05:002008-01-30T09:28:00.000-05:00I've been trying this out in rspec today, it's als...I've been trying this out in rspec today, it's also (sometimes) a nice space saver when you write your specs on a single line:<BR/><BR/>it("should be nil"){@x.should be_nil}<BR/><BR/>Now if we can just get rid of having to write the "should be nil" bit ourselves when it's redundant...Ana Nelsonhttps://www.blogger.com/profile/07952731641901031606noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-61311540474576176342008-01-30T03:05:00.000-05:002008-01-30T03:05:00.000-05:00Thanks for this, it's a great example of how to cl...Thanks for this, it's a great example of how to clarify the tests.<BR/><BR/>One objection I've seen from people is that if you were to test-drive it with one expectation per test (rather than starting with the big ol lump and then refactoring into multiple tests), you end up having to edit more than one test for each bit of production code you add.<BR/><BR/>For example, I imagine the order would go like<BR/><BR/>1. Write ReservationService.expects(:reserve_for) expectation in Test #1<BR/>2. Implement call to ReservationService.reserve_for<BR/>3. Write MaidService.expects(:notify) expectation in Test #2<BR/>4. Implement call to MaidService.notify<BR/><BR/>Here's where the problem arises...Test #2 is passing right now, but you have to go back to Test #1 and stub MaidService.notify.<BR/><BR/>I don't really have a problem with that. I think it's perfectly simple. However I've encountered some resistance from people who claim that in making this test pass, I've made a previously passing test turn red. Is there merit to that argument, or do you think it might just be an excuse to avoid mock-based testing?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-61411414446504552362008-01-29T23:42:00.000-05:002008-01-29T23:42:00.000-05:00The other week on the rspec list David Chelimsky p...The other week on the rspec list David Chelimsky posted this http://pastie.caboo.se/142403 which I think is really cool for using mocks and doing the one assertion per test. Not sure if this helps with the controller issues you guys have been having?<BR/><BR/>Cheers<BR/>ShaneAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-70145765406880092212008-01-29T22:13:00.000-05:002008-01-29T22:13:00.000-05:00Jack, I can't stand trying to test controllers. It...Jack, I can't stand trying to test controllers. It's incredibly painful. I'll post something new in the near future, but the situation is largely the same as the previous post you already linked to.<BR/><BR/>I wish I had better news.<BR/><BR/>Cheers, JayAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-15775955817829077232008-01-29T13:29:00.000-05:002008-01-29T13:29:00.000-05:00I went through a very similar exercise yesterday w...I went through a very similar exercise yesterday with a coworker. I'm a big fan now of the style you describe as its definitely made the specs cleaner and clearer. I'd really suggest trying this out to anyone who's skeptical. <BR/><BR/>@jay: At the moment I feel comfortable with the 'how' but sometimes get stuck at the 'what', especially with controllers. At times I've written a spec for almost every line of a controller and that just feels wrong; at the same time, I don't want to be too broad and not catch situations where people are changing instance variable names and screwing everything up. Do you have any guidelines or 'mantras' if you will about what types of calls you test and what you avoid? Have you had any new ideas or thoughts since http://blog.jayfields.com/2007/09/rails-testing-controllers.htmlJack Dempseyhttps://www.blogger.com/profile/17940809933407388413noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-70817220450891723852008-01-29T10:57:00.000-05:002008-01-29T10:57:00.000-05:00Shane,One assertion (or expectation) per test is a...Shane,<BR/><BR/>One assertion (or expectation) per test is a good rule for unit testing and a good guideline for functional testing. If your test requires a fair amount of setup that makes it run slowly, I assume you are writing functional tests, in which case you'll need to balance speed with purity.<BR/><BR/>I would first take a good long look at the code and make sure there's no other way to do what you want to accomplish. There might be a way to extract the slow code to a method, mock that method and turn this into a unit test.<BR/><BR/>If you can't figure out a way to tease things apart, then it's one of those situations where you'll need to go against the suggestion and do multiple asserts. There's no silver bullet. =)<BR/><BR/>Cheers, JayAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-8260071809897098172008-01-29T04:49:00.000-05:002008-01-29T04:49:00.000-05:00Hi Jay ... hey my one issue with the one expectati...Hi Jay ... hey my one issue with the one expectation per test approach is that I often end up performing the same setup/context repeatedly. Now in most cases this is ok and not much of an overhead, but in some circumstances it means having a slower test ... where it's faster to do the Arrange and Act (thinking of Bill Wake's 3 A's here ... Arrange, Act, Assert) once and then do the Asserts.Anonymousnoreply@blogger.com