Juanma Cervera <[EMAIL PROTECTED]> writes: > Hello > > I am still learning to specify named_scopes, having some troubles and > needing some help. > > This is the situation. > I have already spec two or three named_scopes independently. > but now, I want to spec a function that combines somes of these > named_scopes dynamically. > Something like this > > > class Thing > > named_scope foo, ..... > named_scope bar, .... > named_scope baz, .... > > def complex_query(condition1, condition2, condition3) > scope = Thing.scoped({}) > scope = scope.scoped( Thing.foo.proxy_options ) if condition1 > scope = scope.scoped( Thing.bar.proxy_options ) if condition2 > scope = scope.scoped( Thing.baz.proxy_options ) if condition3 > scope > end > end > > I don't know if there is a better way to implement it, but it works. > The problem is with the specification. I don't know how to do it with > mocking. > I have already spec and test the named_scopes against the database and > think > that this time I have to test only the chaining and not the result.
Hey Juanma, I think it might be time to write a custom expectation matcher for this. There's no built-in support for specifing chained method calls like you need (besides chaining mocks) - because chained method calls are usually trainwrecks and trainwrecks are bad! But named_scopes are kinda unique in that it doesn't matter what order they're called in. That is, chained named_scopes are compositional and don't couple the client code to some internal structure. Maybe you want a matcher like Thing.should receive_scoped(:foo, :bar, :baz) so you can do Thing.foo.bar.baz Thing.foo.baz.bar Thing.bar.foo.baz etc or perhaps Thing.should receive_scoped.foo("foo scope param").bar.baz(123) I'm not 100% sure. But, to tell you the truth, I would still avoid that. I'd just spec Thing.complex_query straight up, no mocks, and hitting the db. There are a few reasons: * Using mocks here would mean you're mocking methods on the target object... which is sometimes okay but *usually* wrong. At the very least, it's a smell indicating that you need just the slightest bit of force to compel you to extract it to a new object * This would really be testing implementation details. The fact that the complex_query uses named_scopes is incidental, really - you could imagine writing the query with find_by_sql, doing it all in memory, whatever. Best just to specify that it returns what you want. There aren't any interesting collaborators here you want to isolate your code from * Economics - you can take the hit of slightly duplicated specs (if you add constraints to Thing.foo then you'll have to update its spec as well as Thing.complex_query). Or you could spend your time writing a non-trivial custom matcher that encourages bad habits. Personally, I would go for the straight-forward specification that gives great examples of expected behavior, even if it means there's a tiny bit of duplication. One thing's clear though - you're on the right track! Encapsulating the scope composition in Thing.complex_query is a great idea. See http://evang.eli.st/blog/2008/7/23/when-duplication-in-tests-informs-design for some more of my detailed thoughts on the technique you're using. Cheers, Pat _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users