Crystal: Add named overloads to the contain spec macro for use with key-value pairs.

Created on 3 Mar 2019  路  12Comments  路  Source: crystal-lang/crystal

There was a discussion in the Gitter chat recently surround some confusion around using the contain spec macro. Basically, someone was just trying to do this...

describe "a hash" do
  it "has a particular value" do
    a_hash.should contain "a value"
  end
end

which was naturally always returning false, as Hash#includes? checks for the presence of a key-value pair, rather than either a key or a value. I propose adding required-named-argument overloads to Hash#includes? and refactor contain to be able to forward those on, so that we could do:

describe "a hash" do
  it "has a particular value" do
    a_hash.should contain value: "a value"
    a_hash.should contain key: "a key"
  end
end

All 12 comments

The contain perform an includes? under the ground.
Other options are to rename contain to includes?, or remove it.

I don't like the proposed API which I find confusing and unnecessary, since all of the above mentioned cases are already covered just by using existing Hash methods:

# checking for key presence
hash.has_key?("key").should be_true

# checking for value presence
hash.has_value?("value").should be_true

# checking for key-value pair presence
hash.includes?({"key", "value"}).should be_true
hash.should contain({"key", "value"})

The only case which is not covered is checking for a subset of a Hash:

hash.should contain({
  "key1" => "value1",
  "key2" => "value2",
})

For which I've submitted #7498.

I think that if there's syntax sugar for calling #includes? on an enumerable, then it's just as valid to pass through named arguments as well and allow for the above fancyness.

@dscottboggs note that the call to #includes? in ContainExpectation is not tied in any specific way to Enumerable - it works for String too for example.

My suggestion was simply that contain should forward all arguments and options to the #includes? method *args, **options, and, to make it work, add these overloads to Hash

def includes?(*, key : K)
  has_key? key
end
def includes?(*, value : V)
  has_value? value
end

I agree that the feature that @Sija is suggesting would be useful, but I don't think it's the same feature. Also, as @asterite commented on #7499, we could just do

a_hash.should be > {"a" => "subhash"}

contains ist a generic expectation method and not tied to Hash. Forwarding any arguments to #includes? would keep the interface generic, but in order for it to work would require to add two method aliases to Hash. That won't happen.

The confusion I can see is the contain expectation perform a includes?; they have different names whereas ContainExpectation#match and includes? do both the same thing.
Even the doc says:

Creates an Expectation that passes if actual includes expected (.includes?).

I propose to rename contain to include, and ContainExpectation to IncludeExpectation.

include is a keyword. We went over this a few years ago.

Right, I didn't realize. I don't know if it's english, perhaps be_including?

would require to add two method aliases to Hash. That won't happen.

That was kind of where I thought this feature request would lead. Its unfortunate we can't have the syntax sugar in specs, but I respect the decision to avoid alias methods.

@dscottboggs It's not just that they would be aliases, but also that these aliases would only exist for spec integration. Nobody would use hash.includes?(key: "foo") in regular code instead of hash.has_key?("foo").

These days I'm experimenting with a catch-all assert macro that you would use like assert foo == bar, assert hash.has_key?(foo), etc. There were previous discussions about this but the proposals were too ambitious, I'll try to do something simpler. Maybe with that all of these discussions will end because you would use the same language (no DSL except assert) and the error messages will be clean.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jhass picture jhass  路  3Comments

oprypin picture oprypin  路  3Comments

oprypin picture oprypin  路  3Comments

Papierkorb picture Papierkorb  路  3Comments

ArthurZ picture ArthurZ  路  3Comments