6

I'm very new to Jasmine and ran across a situation where I would expect either a String or null. I attempted to do an or within the toEqual, but I'm seeing some odd results which leads me to believe I'm going about this the wrong way. What is the best way to go about a situation like this?

Perhaps I'm just going about my testing wrong. Should I just scrap this idea of having one test to test both situations?

describe("Jasmine", function () {

    //1
    it("should be able to handle any(String) || null within toEqual for string", function () {
        expect("aString").toEqual(jasmine.any(String) || null);
    });

    //2
    it("should be able to handle any(String) || null within toEqual for null", function () {
        expect(null).toEqual(jasmine.any(String) || null);
    });

    //3
    it("should be able to handle null || any(String) within toEqual for string", function () {
        expect("aString").toEqual(null || jasmine.any(String));
    });

    //4
    it("should be able to handle null || any(String) within toEqual for null", function () {
        expect(null).toEqual(null || jasmine.any(String));
    });
});
  1. Pass
  2. Expected null to equal <jasmine.any(function String() { [native code] })>.
  3. Pass
  4. Expected null to equal <jasmine.any(function String() { [native code] })>.

I realize there is also a toBeNull() which is probably why the results are so wonky, but without an "or" chaining I didn't know how to incorporate it.

(Running Jasmine 1.3.1 revision 1354556913)

Solved! Full solution below if anyone is interested

describe("Jasmine", function () {

    beforeEach(function () {
        this.addMatchers({

            toBeStringOrNull: function () {
                var actual = this.actual;

                this.message = function () {
                    return "Expected " + actual + " to be either string or null";
                };

                return typeof actual === 'string' || actual instanceof String || actual === null;
            }

        });
    });

    //1
    it("should be able to handle any(String) || null within toEqual for string", function () {
        expect("aString").toBeStringOrNull();
    });

    //2
    it("should be able to handle any(String) || null within toEqual for null", function () {
        expect(null).toBeStringOrNull();
    });

    //3
    it("should be able to handle null || any(String) within toEqual for string", function () {
        expect("aString").toBeStringOrNull();
    });

    //4
    it("should be able to handle null || any(String) within toEqual for null", function () {
        expect(null).toBeStringOrNull();
    });
});
JackMorrissey
  • 2,567
  • 2
  • 21
  • 18
  • Would you expect a single input to give you both `string` or `null`? I don't understand why a single `expect` would need to cover both cases. – Evan Davis Feb 22 '13 at 17:53
  • @Mathletics I would expect either, which is why I'm hitting this wall. I was trying to create a test so that during integration testing I could reuse this for both cases. I think I'm realizing that this is not the standard way to go about that. – JackMorrissey Feb 22 '13 at 18:11
  • Can you show an example of a method which, from a single input, could potentially return both? – Evan Davis Feb 22 '13 at 19:15
  • 1
    @Mathletics I was receiving data from a server for different items which sometimes had a populated string, otherwise it was null. Those were the two possible outcomes, so I wanted to test for that. I thought it would have been neat for some reuse in integration testing. – JackMorrissey Feb 22 '13 at 21:35

2 Answers2

12

Write your own custom matcher:

toBeStringOrNull: function() {
  var actual = this.actual;

  this.message = function () {
    return "Expected " + actual + " to be either string or null";
  }

  return typeof actual === 'string' || actual instanceof String || actual === null;
}
tekumara
  • 8,357
  • 10
  • 57
  • 69
1

Provided that you definitely want those cases to work on the same test, you could write your own matcher. Something like

toBeStringOrNull: function() {
  var actual = this.actual;

  this.message = function () {
    return "Expected " + actual + " to be either string or null";
  }

  return jasmine.any(String) || actual === null;
}
Felipe
  • 440
  • 3
  • 18
  • Spectacular, thanks. I think I'll try to avoid what I was attempting, but it's great to know I can accomplish something like this in Jasmine fairly easily. – JackMorrissey Feb 22 '13 at 21:30
  • 1
    This doesn't work. returning jasmine.any(String) means the above matcher always returns true. See http://stackoverflow.com/questions/8947787/object-equality-in-jasmine-custom-matcher for more on how to use one matcher inside another. – tekumara Feb 25 '13 at 23:10
  • @tukushan You're absolutely correct. Thanks for the correction and the pointer. – JackMorrissey Feb 26 '13 at 14:41