Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: add 'properties' assertion #72

Closed
mullr opened this issue Jun 4, 2012 · 12 comments
Closed

Feature request: add 'properties' assertion #72

mullr opened this issue Jun 4, 2012 · 12 comments

Comments

@mullr
Copy link

mullr commented Jun 4, 2012

There's a pretty common case that the existing deep.equal, deep.property, and contains.keys APIs don't cover, namely asserting that an object should have some properties of some given values. Namely, I'd like to be able to say:

obj = {...};
expect(obj).to.have.properties({
  a: 42, 
  b: "someVal"
});

obj is allowed to have other properties as well, but fails if a or b is absent or has a different value.

Here's the coffeescript I hacked up to do this locally, which barely works:

chai.Assertion.addMethod 'properties', (expectedPropertiesObj) ->
  args = ([key,val] for own key,val of expectedPropertiesObj)
  args = _.flatten(args)
  this.property.apply(this, args)
@domenic
Copy link
Contributor

domenic commented Jun 4, 2012

So to be clear, this would be sugar for

expect(obj).to.have.property("a", 42);
expect(obj).to.have.property("b", "someVal");

?

@mullr
Copy link
Author

mullr commented Jun 4, 2012

Yes, exactly.

@logicalparadox
Copy link
Member

I am not particularly sure that properties is the way to go about handling this sort of scenario. Namely because you would get those who want to do the following:

expect(obj).to.have.properties({
    a: {
        key: 'answer to life, the universe, and everything'
      , value: 42
    }
  , b: [ 'hello', 'universe' ]
});

Your getting into the area of deep equality and it could be confusing to an end user as to which assertion to use. I like the concept, but I'm not sure if properties... let alone chai's core... is the right place for this.

Let's leave this open for more discussion... Please chime in if you have any ideas.

@mullr
Copy link
Author

mullr commented Jun 5, 2012

That's a good point. I guess in such a case I would expect the properties to be checked against the whole object tree, kind of like a big destructuring bind. For example:

expect(obj).to.have.properties({
    a: {
        key1: 'val1', 
        key2: 'val2'
    }
  , b: [ 'hello', 'universe' ]
});

Would pass if given

var obj = {
  a: {
    key1: 'val1', 
    key2: 'val2', 
    otherProperty: 'otherValue'
  }, 
  b: ['hello', 'happy', 'universe']
};

This would be pretty cool, since you could use it to make very succinct checks against the whole object tree. The array check is a little odd, since it's different from regular destructuring. But I think it makes sense in this context. Maybe expect.to.match.structure.of or similar would be a better name.

I'm curious to know your thoughts on this; if it doesn't belong in core (which I could easily believe), do you think it can be easily added in a plugin? I had some trouble before trying to access the 'util' object, which seems necessary to write any new kind of assertion that doesn't build solely on existing ones.

@logicalparadox
Copy link
Member

Plugin is probably the best way to go, and this is a great use case for one. In short, a plugin is no more than a function that can be useed by Chai.

chai.use(function (_chai, utils) {
  // ...
});

We have found that the best way to implement is have a single module.exports that is the function that can be used, as you can see here. Also, here is the documentation for all the utils.

To me, it sounds like your building an object diff function, then checking the diff to make sure all of the imbalance is of the additive nature. Well, that is how I would approach it.

@logicalparadox
Copy link
Member

Did you find a solution to this?

@mullr
Copy link
Author

mullr commented Jun 17, 2012

Not yet, my work has shifted to other areas for the moment. I'll re-approach it from the plugin perspective when I next deal with the JS part of my system.

On Jun 18, 2012, at 8:44 AM, Jake Luer wrote:

Did you find a solution to this?


Reply to this email directly or view it on GitHub:
#72 (comment)

@logicalparadox
Copy link
Member

Ok. Closing for now but feel free to re-open if you run into issues when you get there.

@al6x
Copy link

al6x commented Jul 6, 2012

As far as I understand - the problem is that there's advanced functional like expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha') and it's not clear how it would behave in case of multiple properties check?

My statistics - 95% of time I check for simple, not nested properties, and usually there are multiple properties i'm interested in.

Basically the problem here as I see it: we can't add support for simple frequently used feature because it breaks complex, rarely used feature (but it's only my opinion, maybe I'm wrong).

@logicalparadox
Copy link
Member

In your scenario, it has more to do with deep.equal than deep.property. If you do:

expect(obj).to.have.property({ one: 1, two: 2 });

Is this all encompassing... I only expect these two properties? If thats the case, then deep.equal is a more appropriate usages. If it isn't.. check only these two properties and assume their are more... then, to retain the current behavior we would have to decide what to change the "subject" of the assertion to. property changes the subject of the assertion for everything there after in the chain so you can do cool things like this:

var obj = { one: [ 'a', 'b', 'c', 'd' ] };
expect(obj).to.have.property('one')
  .an('array')
  .with.length.above(3)
  .and.include.keys('a', 'b');

Which means, we would be changing the behavior of an assertion without a flag. We want to leave that to "plugins only" as much as possible.

Right now, if I am continually testing the structure of a similar object in several different tests, I write a helper plugin. If it's different objects, I agree that it can be tedious to write out 'to.have.property' many times if that is the situation.... But, I am still not convinced that property/ies is the place to fix this. Personally, I think schema's is the way to go about this, but there are varied implementations of this so definitely a problem to solve in plugins.

@Zammy
Copy link

Zammy commented Jan 22, 2015

Is there plugin that does this? I just realized I have written wrong tests (they were passing although they were not doing anything). Maybe add another base test like ".properties". I am not experienced with chai but maybe you need feedback from people like me. :) I now need to write .property for each of the properties I want to check.

(Update1)
It seems that http://chaijs.com/plugins/chai-subset is doing what I want.
(Update2)
Of course I cannot make it work with chai-things sigh.

@eps1lon
Copy link

eps1lon commented Oct 1, 2020

to.include works exactly like you want:

When the target is an object, .include asserts that the given object val’s properties are a subset of the target’s properties.

expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});

-- https://www.chaijs.com/api/bdd/#includeval-msg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants