How to Test an optional member of an object?

What is the best technique to test an optional object member. Right now we are prefacing the expect statements with an if:

 if(object.member) expect(object).to.have.a.property('member').that.is.a('string');

but there must be a method that is more inline stylistically. E.g.

 expect(object).to.have.an.optional.property('member').that.is.a('string');

or (adding would as an empty chain, for readability):

 expect(object).to.have.an.optional.property('member').that.would.be.a('string');

or (moving the optional to provide an alternative version of expect):

 optionally.expect(object).to.have.a.property('member').that.is.a('string');

update - I started to write this code (new to chai) to see if I could accomplish what I was targeting, so I added a small plugin:

module.exports = function(chai, utils) {
    var Assertion = chai.Assertion
        , i = utils.inspect
        , flag = utils.flag;

    var OPTIONAL_FLAG = 'chai-optional/option'
    Assertion.addProperty('optional', function() {
        flag(this, OPTIONAL_FLAG, true)
        return this;
    })

    Assertion.overwriteMethod('property', function (_super) {
        return function assertProperty (propertyName) {
            if (flag(this, OPTIONAL_FLAG)) {
                flag(this, OPTIONAL_FLAG, false) ;

                var obj = this._obj;
                var isPropertyPresent = (obj[propertyName]) ? true : false ;

                if(isPropertyPresent) {
                    return _super.apply(this, arguments);
                }

            } else {
                _super.apply(this, arguments);
            }
        };
    });

    Assertion.addProperty('would', function () {
        return this;
    });
};

WIth usage:

it('could be null or have a value', function(done){
    var objWithout = {}
    var objWith = {}
    objWith.someProperty = 'blah'

    expect(objWith).to.have.optional.property('someProperty').that.would.be.a('string');
    expect(objWithout).to.have.optional.property('someProperty').that.would.be.a('string');

    return done();
})

The current problem even when the property isn't present, the control of the function ends - but the evaluation chain continues. I need to end the evaluation with out a failing assertion - is this possible?


update either solution (simplistic solution):

var either = function(firstCondition){
    var returnObject = {}

    try{
        firstCondition()
        returnObject.or = function(secondCondition){ return }
    } catch(e) {
        returnObject.or = function(secondCondition){ return secondCondition() }
    }
    return returnObject ;
}

module.exports = either

I think the implementation is a little clunky - but fat arrow functions will hap thin out some of the syntax. So here is waiting on that!

The current problem even when the property isn't present, the control of the function ends - but the evaluation chain continues. I need to end the evaluation with out a failing assertion - is this possible?

After having read about chai's plugin guide I would have used a similar approach with a flag. However, I have reached the same conclusion - you cannot simply stop a chain.

A possibility I though of would be not only to implement new properties and a new flag, but to overwrite the assert method itself - to not throw when the OPTIONAL_FLAG flag on the current Assertion object is set. However, the chance to destroy everything or to miss an edge case is too hight.

After all, I don't think it's a good idea. Citing from this "confusing syntax" issue:

I think the misunderstanding comes from your expectation that Chai follows most/all English grammar rules. Unfortunately, English grammar has way too many rules (and exceptions to those rules) for it to be a reasonable undertaking to implement.

The challenge with designing Chai's chain-able assertions is finding the balance between being expressive and concise. Even if full grammar wasn't a daunting task to implement and document, it would make the API less concise, which is not good for a testing environment.

RULE: Any "flag" which modifies the behavior of an assertion (negation not or inclusion include/contain , etc...), once set to true should remain true until the end of the chain.

This means it is impossible to implement something like an .or operator as well.

What is possible though would be to implement something like

either(function(){
    expect(object).not.to.have.a.property('member');
}).or(function(){
    expect(object).to.have.a.property('member').that.is.a('string');
});

Maybe one can build a more appealing syntax on that.