1

I have the following method in my test app:

def on(definition, visit = false, &block)
  if @page.is_a?(definition)
    block.call @page if block
    return @page
  end

  if @context.is_a?(definition)
    block.call @context if block
    @page = @context unless @page.is_a?(definition)
    return @context
  end

  @page = definition.new(@browser)
  @page.view if visit

  @page.correct_url? if @page.respond_to?(:url_matches)
  @page.correct_title? if @page.respond_to?(:title_is)

  @model = @page

  block.call @page if block

  @page
end

When I run the rubocop tool against the file containing this method, I get the following responses:

C: Cyclomatic complexity for on is too high. [10/6]
C: Perceived complexity for on is too high. [10/7]

What I don't get is what it thinks is "too complex" thus I'm having trouble figuring out how to resolve the issue. Ideally I'd rather not just tell rubocop to avoid the warning since it's no doubt telling me something useful.

As far as the method being complex, as you can see, I have a couple of if calls and then I have to work with the @page object in order to make sure it is set up correctly. (This example, in context, is a Watir-WebDriver object.)

I do agree the method is complex in that it requires checking on if @page is already existing and set to something as well as checking if the @page should be the same as the @context. But -- again, I'm not sure what to do about it.

The full code for the module that this method is inside is here:

https://github.com/jnyman/symbiont/blob/master/lib/symbiont/factory.rb

I was initially thinking I could just break this up into different method calls, which might reduce the complexity of each method. But then that means someone reading my code has to hop to a series of different methods to understand what on is doing. Just moving things around wouldn't seem, to me, to be removing complexity overall; rather, it would just be shuffling it around. Or am I wrong?

Any advice here is appreciated.

UPDATED CODE

I have gotten this reduced somewhat. Here's what I now have:

def on(definition, visit = false, &block)
  if @page.is_a?(definition)
    block.call @page if block
    return @page
  end

  if @context.is_a?(definition)
    block.call @context if block
    @page = @context
    return @context
  end

  @page = definition.new(@browser)
  @page.view if visit

  @model = @page

  block.call @page if block

  @page
end

Based on feedback, I removed an unless qualifier that did seem to be useless. I also removed two lines that I found I could put to better use somewhere else (checking the title and the url).

This has removed the "perceived complexity" entirely and has left me with just this:

C: Cyclomatic complexity for on is too high. [7/6]

I appear to be "one point" (or whatever the terminology is) too complex.

OmG
  • 18,337
  • 10
  • 57
  • 90
Jeff Nyman
  • 870
  • 2
  • 12
  • 31
  • Your code screams that it can't infer the object identify/class type throughout it. You're constantly asking "hey are you this? do you know how to do that?". It's fine to safe guard but likely you should be validating your input somewhere else before this method can be invoked. – Anthony Oct 03 '15 at 13:30
  • Hmm. I don't see how I could, though. (Which is _not_ me saying it's not possible!) The issue is that the context factory `on` method is called like this: `on(SomePage)`. This can happen anywhere in a test script. Further, you can have `on(SomePage)` and then `on(SomeOtherPage)`. It's important for the logic to know that SomeOtherPage is not SomePage. But you can also do `on_new(SomePage)` which means SomePage is SomePage but a new -- different -- instance of it. So the challenge is I don't know what test writer is going to do. Still, taking your words to heart, I'll see if I can clean it. – Jeff Nyman Oct 03 '15 at 13:46

1 Answers1

3

There are places where your code is redundant. For example, you have this:

if @page.is_a?(definition)
  ...
  return ...
end

which means that, in any part to follow this, you can assume that @page.is_a?(definition) is not true, unless you modify @page after this part. Nevertheless, you have:

if @context.is_a?(definition)
  ... unless @page.is_a?(definition)
end
sawa
  • 165,429
  • 45
  • 277
  • 381
  • Ah! Good point. Yes, that unless qualifier does not seem to be needed. If I take that out, I do remove one "point" from my complexity according to rubocop. That puts me at `Cyclomatic complexity for on is too high. [9/6]` and `Perceived complexity for on is too high. [9/7]`. Interesting. So it seems the complexity is based on my conditionals, at least to some extent. – Jeff Nyman Oct 03 '15 at 14:09
  • 2
    It's been awhile and I realize I didn't accept anything to this. This answer certainly put me on the path to understanding where I needed to place my emphasis; hence accepting (immediately way overdue). – Jeff Nyman May 12 '17 at 21:49