Mocking the dynamic composition of named_scopes

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.

Juanma C.

Juanma C. [email protected] writes:

scope = scope.scoped( Thing.bar.proxy_options ) if condition2

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

for some more of my detailed thoughts on the technique you’re using.

Cheers,
Pat

I think you’re right Pat.
Thanks for your answer.

Juanma