Cucumber - step negating another expecting step


#1

hey there,
this here may be a little too general, and maybe is only a though
sharing,
but would be nice to hear what you think.

What is your opinion about expectation steps that negates another
expectation step?

for example: let’s say I have a step that specs something:

Then /^I should see the people search form$/ do
response.should have_tag(‘form#search’)
# this of course could get more complex, like several expectations
in a
row or so
end

then I would like to reuse the step but negating it, like:

Then /^I should not see the people search form$/ do
!(Then “I should see the people search form”)
end

this wont work because spectations (or some of them) of the first step
will
fail. A low level implementation of this could be something like:

Then /^I should (.*) see the people search form$/ do |bool|
post_fix = “_#{bool}” if bool
# allow the incompleteness of this line, but you get the idea :slight_smile:
# it’ll send should or should_not
response.send(“should#{postfix}”, have_tag(‘form#search’) )
end

but here there are considerations regarding unreadability of the step,
but
anyway the question again:

would this be too bad? too problematic? what do you think about
expectation
steps negating another expectation steps?

cheers
joaquin


#2

at the moment I do it this way, hiding the complexity out of the steps:

Then /^I should see the people search form$/ do
people_search_form_exists
end

Then /^I should not see the people search form$/ do
people_search_form_exists “not”
end

and then the method:

def people_search_form_exists negation = “”
neg = “_not” unless negation.blank?
response.send “should#{neg}”.to_sym, have_tag(‘form#frmSearch’)
end

this is a simple case, but what do you think about this? any blog post
or so

cheers
joaquin


#3

On 16 Apr 2009, at 11:22, Joaquin Rivera P. wrote:

and then the method:

def people_search_form_exists negation = “”
neg = “_not” unless negation.blank?
response.send “should#{neg}”.to_sym, have_tag(‘form#frmSearch’)
end

this is a simple case, but what do you think about this? any blog
post or so

Yeah this is an annoying one isn’t it. I sometimes get around it by
going old-skool and pulling out the Test::Unit assertion methods
instead:

 Then /^I (should|should not) see the people search form$/ do |

maybe|
has_matching_tags = current_dom.css(‘form#frmSearch’).length > 0
assert has_matching_tags == (maybe == “should”)
end

I think that would work. It’s shorter, but is it much easier to
understand?

Matt W.
http://blog.mattwynne.net
http://www.songkick.com


#4

thanks matt,
yes, the regexp in the step matcher is a good one to dry it up

So I end up with this one:

Then /^I (should|should not) see the people search form$/ do |maybe|
people_search_form_should_exist maybe == “should”
end

and the method:

def people_search_form_should_exist it_should_exist
_not = “_not” unless it_should_exist

response.send “should#{_not}”.to_sym, have_tag(‘form#frmSearch’)
end

only because I find it easier to read (when I don’t need to jump to the
method), but yours maybe faster (shorter it is), I could come back to it
later and benchmark both

thanks again,
joaquin


#5

wow! even shorter :slight_smile:

2009/4/16 Matt W. removed_email_address@domain.invalid


#6

On 16 Apr 2009, at 14:06, Joaquin Rivera P. wrote:

def people_search_form_should_exist it_should_exist
_not = “_not” unless it_should_exist

response.send “should#{_not}”.to_sym, have_tag(‘form#frmSearch’)
end

only because I find it easier to read (when I don’t need to jump to
the method), but yours maybe faster (shorter it is), I could come
back to it later and benchmark both

If you don’t mind using the #send (I was trying to help you get rid of
it) then just do this:

Then /^I (should|should not) see the people search form$/ do |maybe|
response.send maybe.underscore.to_sym, have_tag(‘form#frmSearch’)
end

thanks again,
joaquin


rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users

Matt W.
http://blog.mattwynne.net
http://www.songkick.com


#7

I’ve been doing something similar. I think the benefit of having half
the steps(each can be negated) wins over the small impact it has on
step readability. Personally I started adding stuff like this(perhaps
not as DRY but simple enough):

Then /^the correspondence should (not )?have inclusions$/ do |negate|
if negate
@outcorr.inclusions.should be_empty
else
@outcorr.inclusions.should_not be_empty
end
end

-lenny


#8

end
end

What’s the advantage of having half the steps?
They should be grouped in a nice way in files anyway.

This one’s quite readable, though.

yes, the regexp in the step matcher is a good one to dry it up

So I end up with this one:

Then /^I (should|should not) see the people search form$/ do |maybe|
people_search_form_should_exist maybe == “should”
end

should … exist … maybe… should

Unreadable?

and the method:

def people_search_form_should_exist it_should_exist
_not = “_not” unless it_should_exist

response.send “should#{_not}”.to_sym, have_tag(‘form#frmSearch’)
end

Convoluted.

end
If you go with this kind of abstraction, this solution and the topmost
in this mail at least do not introduce extra methods to deal with the
should/should not. more methods to have fewer steps can not be an
advantage,
I think.

But the readbility of

Then /^I should see the people search form$/ do
response.should have_tag(‘form#peopleSearch’)
end

Then /^I should not see the people search form$/ do
response.should_not have_tag(‘form#peopleSearch’)
end

is higher for me, as I only need to think what the have_tag means,
but do not have to parse the should/not, send, underscore and to_sym.

The difference in readability is from about 2 seconds to at least
10 seconds. That’s a disadvantage that I’m not willing to pay for
any advantage of having “fewer steps”.

Bye,
Kero.


How can I change the world if I can’t even change myself?
– Faithless, Salva Mea


#9

2009/4/17 Lenny M. removed_email_address@domain.invalid

end

Thanks Lenny. You get it. Readability trumps obsessive DRYness.
Corollary: The value of DRYness diminishes with distance.


#10

end
end

What’s the advantage of having half the steps?
They should be grouped in a nice way in files anyway.

This one’s quite readable, though.

[snip]

end
any advantage of having “fewer steps”.
I use the first method for negating steps - when I need it. The
advantage
is DRY (“Don’t Repeat Yourself”). While your method - having two
separate
step matchers - may be a little easier to comprehend, my worry would be
maintainability. If another user (perhaps just future-me) comes along
and
adds another step matcher between those two without noticing that they
negate each other, and then a third user (perhaps future-future-me)
comes
along and changes one step matcher but not the other, we no longer truly
have negation.

There is definitely a balancing act. Hopefully future-me is less likely
to
change one branch of my negating step matcher without mirroring the
change
in the other half, it is even less likely in some of the one-line
solutions
that are more difficult to quickly comprehend.

If you value comprehension and are confident that you’ll remember to
update
both cases together, then your solution is ideal. If you value
idiot-proofing and don’t mind slower comprehension, then a one-line
solution
may be ideal. I worry about everything and therefore take the middle of
the
road solution.

      /\/\ark

__________ Information from ESET NOD32 Antivirus, version of virus
signature
database 4022 (20090420) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com


#11

well, that was the vague idea I had when asking at first, but I don’t
think
every multi-should or should_not step can be in general negated,

by the way, Lenny’s one have come to be my preferred solution

joaquin


#12

On Wed, Apr 22, 2009 at 1:17 AM, aslak hellesoy
removed_email_address@domain.invalidwrote:

@outcorr.inclusions.should be_empty

else
@outcorr.inclusions.should_not be_empty
end
end

Thanks Lenny. You get it. Readability trumps obsessive DRYness.
Corollary: The value of DRYness diminishes with distance.

This is just a thought, but every solution on this thread, including
this
one, indicates to me that something is amiss.

I don’t recall ever seeing a “Then” step without a “should” or “should
not”
(I’ve only come across steps in English so far). To me, this means
there’s
something special about “Then” steps, and quite possibly the “should” /
“should not” aspect of it should be built into the cucumber grammar.

I’ll be the first one to agree that cucumber steps are not the place to
get
cute, and that readability is paramount, but seriously, shouldn’t you be
able to negate the core expectation of a step without having to write
extra
code?

Jeff


#13

ok, let’s say:

Then /should see “(.+)”/ do |text|

a binary step, easily negable

end

Then /I deny some steps/ do

really not so readable

Not Then “should see “your are not log in””

Then “some other step that holds”

another way of saying it bad

But Not Then “should receive info for non users”

or maybe this bad

And Not Then “should receive info for non users”
end

now comes the mars attack part, I haven’t dig into cucumber or rspec
code
that much, only guessing here.
and Not could do something like receiving the step that comes behind the
keyword

def Not(step)
# we tell the step to assert (I mean the rspec synonym) itself
# so that it raises some RSpec exception
step.assert!
# the step really passed, we should raise an Exception not
rescueable
for the next rescue
raise Spec::UncatchableException
rescue Spec::Expectations # or something like that
# green, nothing, this is what we expected
end
end

is this doable? I have to say I have not come to need this that bad to
ask
the cucumber to provide it

is really late and this cross my mind :slight_smile:

joaquin


#14

On Thu, Apr 23, 2009 at 12:34 AM, Joaquin Rivera P.
removed_email_address@domain.invalidwrote:

Then “some other step that holds”
and Not could do something like receiving the step that comes behind the
# green, nothing, this is what we expected
end
end

is this doable? I have to say I have not come to need this that bad to ask
the cucumber to provide it

Only if you have exactly one thing that can be negated. If you have
several
things, this won’t work.

Frankly, I don’t think I’ll ever implement a cute feature in Cucumber
that
allows you to have out of the box negation. It would be ugly no matter
how
it’s done, and it would only save you a line or 2 of code.

Aslak


#15

On Thu, Apr 23, 2009 at 1:53 AM, aslak hellesoy
removed_email_address@domain.invalidwrote:

Frankly, I don’t think I’ll ever implement a cute feature in Cucumber that
allows you to have out of the box negation. It would be ugly no matter how
it’s done, and it would only save you a line or 2 of code.

fwiw, +1 on that. refactor to a descriptive method and get used to
refactoring cucumber implementations to keep the cukes dry. moist cukes
get
moldy - my grams taught me that.


#16

Frankly, I don’t think I’ll ever implement a cute feature in Cucumber that

allows you to have out of the box negation. It would be ugly no matter how
it’s done, and it would only save you a line or 2 of code.

fwiw, +1 on that. refactor to a descriptive method and get used to
refactoring cucumber implementations to keep the cukes dry. moist cukes get
moldy - my grams taught me that.

totally agree


#17

On Wed, Apr 22, 2009 at 3:51 PM, Jeff T.
removed_email_address@domain.invalidwrote:

but simple enough):
Corollary: The value of DRYness diminishes with distance.

I’ll be the first one to agree that cucumber steps are not the place to get
cute, and that readability is paramount, but seriously, shouldn’t you be
able to negate the core expectation of a step without having to write extra
code?

It’s an interesting idea. Could you give an example of how you’d like it
to
look? How would you like to use this? A step definition example would be
great.

Aslak


#18

On Thu, Apr 16, 2009 at 10:35 AM, Matt W. removed_email_address@domain.invalid wrote:

end
method), but yours maybe faster (shorter it is), I could come back to it
later and benchmark both

If you don’t mind using the #send (I was trying to help you get rid of it)
then just do this:

Then /^I (should|should not) see the people search form$/ do |maybe|
response.send maybe.underscore.to_sym, have_tag(‘form#frmSearch’)
end

I’m definitely on the “clarity trumps DRY” side of the fence here. But
that doesn’t deny that there is a problem to solve. This solution gets
close but is still takes me some extra time to grok. What extracting
that to a method like this:

Then /^I (should|should not) see the people search form$/ do
|should_or_should_not|
expect_that(response, should_or_should_not,
have_tag(‘form#frmSearch’))
end

def expect_that(target, should_or_should_not, matcher)
target.send should_or_should_not.underscore.to_sym, matcher
end

I definitely don’t see this in Cucumber, BTW as “should” and “should
not” are not the only way to express positive and negative
expectations even in English, let alone other languages that might not
deal w/ negation in such clean and consistent ways.

And it’s not the least number of characters you can type. But it’s
grok-able IMO. And DRY. And cute, to boot.

Of course, if anybody can come up with one word other than “maybe”
(damn you Ben M. for f’ing up my ability to type that word) to
express should_or_should_not more succinctly, that might help reduce
the typing. “Maybe” doesn’t work for me because it implies lack of
precision rather than choice (to me).

Don’t know how helpful this is, but it’s fun to explore.

David


#19

David C. wrote:

Then /^I (should|should not) see the people search form$/ do |maybe|

grok-able IMO. And DRY. And cute, to boot.

Of course, if anybody can come up with one word other than “maybe”
(damn you Ben M. for f’ing up my ability to type that word) to

LOL… I seem to have that effect on people. :slight_smile: But, think how I feel.
Do you know how many “witty” people I have met who tell some “maybe”
joke and think they are the first person to ever make it? Well, I can
tell you that it does, in fact, get very old. :slight_smile:

express should_or_should_not more succinctly, that might help reduce
the typing. “Maybe” doesn’t work for me because it implies lack of
precision rather than choice (to me).

Back on topic… I like this proposal the best. Having the
“should_or_should_not” makes it very clear what is happening. To reduce
typing I suppose you could call it “expectation” in the step
definition… and you could possibly even abbrevaite that more. But I
wold probably stick with the more verbose “should_or_should_not”. I
also agree that this is something that shouldn’t be part of Cucumber
itself but I’ll probably play around with it in my own projects.

-Ben Mabey


#20

On Thu, Apr 23, 2009 at 9:39 AM, Ben M. removed_email_address@domain.invalid wrote:

yes, the regexp in the step matcher is a good one to dry it up
_not = “_not” unless it_should_exist
it)
that to a method like this:
I definitely don’t see this in Cucumber, BTW as “should” and "should

Back on topic… I like this proposal the best. Having the
“should_or_should_not” makes it very clear what is happening. To reduce
typing I suppose you could call it “expectation” in the step definition…
and you could possibly even abbrevaite that more.

e9n ???
s18t ???