On Jan 11, 2008 5:11 PM, Jay Donnell <[EMAIL PROTECTED]> wrote: > >> The thing is that, ideally, you don't want to have to make changes to > >> the tests for object A when you're refactoring B. > >> > >> WDYT? > > > Yeah, I buy that. Not everyone does though. Or at least not everyone > > feels that it's a particularly important goal. > > I think the fear many of us classicists have is that if A uses B (and Bs > interface changes) then A will pass but it shouldn't. We also have to go > around updating all the mocks for tests that use B. With a state based > approach we wouldn't have to change any tests assuming we've fixed the outer > methods already. If we haven't then our tests will fail and tell us we need > to.
As David has mentioned before, that's a matter of tool support. Consider how this works if you're using Java. You might have some interface that looks like: public interface Account { void deposit(int amount); void withdraw(int amount); } You've got a BankAccount class that implements it, and in tests that depend on the Account role, you use mock objects. If you were to rename deposit to credit, and withdraw to debit, the first thing that would happen is that the compiler would complain that BankAccount doesn't implement the Account interface anymore. So you can lean on the compiler to fix it. Even better, you would use a refactoring tool, and that'd look for any classes that implement Account and change their method names. Conceptually, the same exact thing is possible and desirable in Ruby. There are some extra hoops to jump through because we can't lean on the compiler like we can in Java. Otoh, we write Ruby a lot faster than Java, and we don't want to stab ourselves. So at this point it's still a worthwhile tradeoff (to me). One problem is that in a language with interfaces, roles are made explicit. In Ruby, any object can play any role, as long as it implements the necessary protocol. Maybe we could do something at the framework level to make the roles more explicit. Something like describe AccountService, " transferring between two accounts" do before(:each) do @source = mock_role(:account) @target = mock_role(:account) @service = AccountService.new end it "should debit the source account" do @source.should_receive(:debit).with(50) @service.transfer 50, @source, @target end it "should credit the target account" do @target.should_receive(:credit).with(50) @service.transfer 50, @source, @target end end Then you have a spec somewhere that verifies that the Account class fulfills the account role. It's an interesting idea, and I remember it being bounced around a while back, as something like "mock integration mode." There are plenty of obstacles though...you can't just use respond_to? on an instance of a class, because objects could use method_missing, be decorated with behavior at runtime, etc. It also doesn't seem practical to try to define instances manually and then swap them in to tests. You probably have to create so many objects for different edge cases that it makes more sense just to use state-based verification in the first place. This latter set of issues is just conjecture though, as I've never given it a shot myself. At this point, we can just use integration tests to catch the things we miss, and be thankful that even large Rails projects seldom pass 25k lines of code :) If demand increases for solutions to the mock maintainability problem, one will arise. > I know, our integration tests should catch this. I'm curious what you guys > mean by "integration tests" in this case. For example, in a rails app are the > 'integration tests' always or usually testing the full rails stack by sending > web requests and verifying against the response? I.e. simulating a user > interaction? In Rails apps, yes, a lot of the stories would go through the full Rails stack. They don't have to though. User stories represent some facet of behavior which, once completed, provides value to the customer. In short, working software. By definition, then, stories must use real production implementations of objects instead of mocks. This means they work quite nicely as integration tests as well. Pat _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users