105

Building on this tutorial testing an angularjs app with chai, I want to add a test for an undefined value using the "should" style. This fails:

it ('cannot play outside the board', function() {
  scope.play(10).should.be.undefined;
});

with error "TypeError: Cannot read property 'should' of undefined", but the test passes with the "expect" style:

it ('cannot play outside the board', function() {
  chai.expect(scope.play(10)).to.be.undefined;
});

How can I get it working with "should"?

thebenedict
  • 2,539
  • 3
  • 20
  • 29
  • 2
    It is easy if you will use "assert", you can do it as `assert.isUndefined(scope.play(10))` – lukaserat Nov 15 '16 at 08:14
  • I don't like any of the answers here, so I went with just using `expect` for undefined checks, `should` for everything else – Kip Feb 03 '22 at 16:06

9 Answers9

87

This is one of the disadvantages of the should syntax. It works by adding the should property to all objects, but if a return value or variable value is undefined, there isn't a object to hold the property.

The documentation gives some workarounds, for example:

var should = require('chai').should();
db.get(1234, function (err, doc) {
  should.not.exist(err);
  should.exist(doc);
  doc.should.be.an('object');
});
David Norman
  • 19,396
  • 12
  • 64
  • 54
  • 16
    `should.not.exist` will validate if the value is `null` so this answer is not correct. @daniel has the answer below: `should.equal(testedValue, undefined);`. That should be the accepted answer. – Sebastian Apr 23 '15 at 18:02
  • 7
    I come to this answer (not the docs ) on a monthly basis :-) – Ralph Cowling Nov 23 '16 at 07:24
62
should.equal(testedValue, undefined);

as mentioned in chai documentation

daniel
  • 1,205
  • 1
  • 10
  • 15
  • 12
    omg, what is there to explain? You expect testedValue to === undefined so you test it. A lot of developers first put testedValue first and then chain it with should and it ends up with error... – daniel May 21 '14 at 14:02
  • 6
    This doesn't work out of the box and it isn't found in [the API documentation for `.equal()`](http://chaijs.com/api/bdd/#method_equal). I can understand why @OurManInBananas asked for an explanation. It's an unexpected use of should as a function accepting two arguments, instead of the expected chained method form accepting a single argument for the expected value. You can only achieve this by importing/requiring and assigning an invoked version of `.should()` as described in the accepted answer by @DavidNorman and in this [documentation](http://chaijs.com/guide/styles/#should-extras). – gfullam Sep 30 '16 at 13:00
  • I think you'll find that that `.equal` syntax produces much better error messages because it allows you to output messages that are more descriptive when things fail – jcollum Nov 10 '16 at 16:57
  • `should().equal(value1, value2)` – imki123 Sep 08 '22 at 01:59
18
(typeof scope.play(10)).should.equal('undefined');
toniedzwiedz
  • 17,895
  • 9
  • 86
  • 131
teddychan
  • 389
  • 2
  • 6
18

Test for undefined

var should = require('should');
...
should(scope.play(10)).be.undefined;

Test for null

var should = require('should');
...
should(scope.play(10)).be.null;

Test for falsy, i.e. treated as false in conditions

var should = require('should');
...
should(scope.play(10)).not.be.ok;
Peter Dotchev
  • 2,980
  • 1
  • 25
  • 19
  • Not especially useful or intuitive, but that's clever! – thebenedict Jun 02 '15 at 12:56
  • 2
    not useful? It's the best answer for undefined testing in bdd style IMO, however it needs to install other npm package (the should package), and I don't think that worth installing other package just for that, awesome answer though – Wagner Leonardi Aug 08 '15 at 17:50
12

I struggled to write the should statement for undefined tests. The following doesn't work.

target.should.be.undefined();

I found the following solutions.

(target === undefined).should.be.true()

if can also write it as a type check

(typeof target).should.be.equal('undefined');

Not sure if the above is the right way, but it does work.

According to Post from ghost in github

Viraths
  • 840
  • 1
  • 13
  • 23
  • 1
    It's worth noting that using this syntax can result in JavaScript interpreting the parenthesized expression as an attempt to invoke the previous line as a function (if you're not ending lines with a semicolon). – bmacnaughton Feb 13 '18 at 18:20
8

Try this:

it ('cannot play outside the board', function() {
   expect(scope.play(10)).to.be.undefined; // undefined
   expect(scope.play(10)).to.not.be.undefined; // or not
});
EpokK
  • 38,062
  • 9
  • 61
  • 69
  • Thanks, but that's what I'm doing in the second attempt above. I'd like to understand how to do it with the [should](http://chaijs.com/guide/styles/#should) syntax. – thebenedict Oct 06 '13 at 13:42
3

Don't forget about a combination of have and not keywords:

const chai = require('chai');
chai.should();
// ...
userData.should.not.have.property('passwordHash');
Yarovoy
  • 649
  • 7
  • 17
1

The answer by @david-norman is correct as per the documentation, I had a few issues with setup and instead opted for the following.

(typeof scope.play(10)).should.be.undefined;

Andy Polhill
  • 6,858
  • 2
  • 24
  • 20
  • `typeof` operator returns a **string**; so this assertion could not be passed; cuz `'undefined' !== undefined` – dNitro Sep 29 '17 at 09:02
0

You can wrap your function result in should() and test for a type of "undefined":

it ('cannot play outside the board', function() {
  should(scope.play(10)).be.type('undefined');
});
pegli
  • 680
  • 8
  • 7