hamjest icon indicating copy to clipboard operation
hamjest copied to clipboard

enhancement request: extend the "hasProperty" idea to arbitrary functions

Open richardwan opened this issue 4 years ago • 5 comments

the "hasProperty" matcher lets you convert from __.assertThat('hello'.length, __.greaterThan(3)) to assertThat('hello', hasProperty('length', __.greaterThan(3))

can we extend this idea to arbitrary functions? so instead of writing __.assertThat(element.querySelector('option').value.length, __.greaterThan(3)) one could write __assertThat(element, evaluates(x => x.querySelector('option').value.length, __.greaterThan(3)))

if there is interest in this enhancement i would be happy to implement it. (I dont like the name "evaluates" and would happily pick a better name as soon as anyone has an alternate suggestions)

richardwan avatar Oct 01 '20 15:10 richardwan

Hmm… interesting idea. The general problem with expressions like x.querySelector('option').value.length is that you just get a TypeError when one of the intermediate properties doesn’t exist or has a different type than expected.

That’s the core problem of most assertion libraries and that’s why all those expect(a.x.c).toBe(…) assertions are (in my opinion) much worse than what hamjest, hamcrest et al can deliver.

That being said, I still don’t have a satisfying solution for function calls in the middle of such chains. I’ve recently added hasDeepProperties() to simplify deeply nested assertions:

__.assertThat(element, __.hasDeepProperties({
    something: {
        value: {
            length: __.greaterThan(3),
        }
    }
});

But there’s still no simple way to do function calls (with arguments) in the middle of such chains. We only have returns(…)/throws(…) for calls without arguments and context. Maybe your suggestion with evaluates(x => …, <matcher>) is a good building block for this:

__.assertThat(element, __.evaluates(e => e.querySelector('option'), __.hasDeepProperties({
    value: {
        length: __.greaterThan(3),
    }
}));

So far, I’ve simply done something like the following in these cases:

__.assertThat(element.querySelector('option'), __.hasDeepProperties({
    value: {
        length: __.greaterThan(3),
    }
});

But that has the same problem mentioned above (causing a TypeError if querySelector doesn't exist, without a way for the assertion library to provide a meaningful description) and it gets worse if the function call is not at the beginning but deeply nested: something.other.querySelector(…).etc

rluba avatar Oct 02 '20 06:10 rluba

And just as a side note: You can use hasSize() to simplify your (and my) example and get better error descriptions:

__.assertThat(element.querySelector('option'), __.hasProperties({
    value: __.hasSize(__.greaterThan(3)),
});

rluba avatar Oct 02 '20 07:10 rluba

__.assertThat(element, __.evaluates(e => e.querySelector('option'), __.hasDeepProperties({
    value: {
        length: __.greaterThan(3),
    }
}));```

I can build "evaluates" and see what I can do for good messaging for the example above. I wonder if it would be helpful to allow an optional "function name/description" parameter that could assist with the messaging.

richardwan avatar Oct 02 '20 13:10 richardwan

This also spawns another "syntactic sugar" matcher "hasNestedProperty". I would be happy to implement if there is interest.

  __.assertThat(element, __.hasNestedProperty('value.length', __.greaterThan(3))

could be syntactic sugar for

  __.assertThat(element, __.hasDeepProperties({
    value: {length: __.greaterThan(3)}
  }))

or maybe

  __.assertThat(element, __.hasProperty(
    'value', __.hasProperty('length', __.greatherThan(3))
  ))

richardwan avatar Oct 02 '20 14:10 richardwan

I’m "sorry" to inform you that this feature is already implemented. 😊 The following should work as expected:

 __.assertThat(element, __.hasProperty('value.length', __.greaterThan(3));

I just noticed that I had never documented that – fixed.

hasDeepProperties is useful when you want to check more than one property at once (which happens in almost all of my tests).

rluba avatar Oct 02 '20 15:10 rluba