Categories
Testing

Jasmine — Spies and Matchers

Testing is an important part of JavaScript.

In this article, we’ll look at how to create more complex tests with Jasmine.

createSpy

We can call createSpy to stub a function.

They can be used to track arguments.

For instance, we can write:

describe("A spy", function () {
  let spy;

  beforeEach(function () {
    spy = jasmine.createSpy('spy');
    spy('foo', 'bar');
  });

  it("tracks that the spy was called", function () {
    expect(spy).toHaveBeenCalled();
  });
});

We created a spy with jasmine.createSpy .

It returns a function that can be watched with Jasmine.

So we can check if it has been called with toHaveBeenCalled .

createSpyObj

We can use the createSpyObj method to create a mock with multiple spies.

For instance, we can write:

describe("Multiple spies", function () {
  let person;

  beforeEach(function () {
    person = jasmine.createSpyObj('person', ['eat', 'drink', 'sleep', 'walk']);
    person.eat();
    person.drink();
    person.sleep(0);
    person.walk(0);
  });

  it("creates spies for each requested function", function () {
    expect(person.eat).toBeDefined();
    expect(person.drink).toBeDefined();
    expect(person.sleep).toBeDefined();
    expect(person.walk).toBeDefined();
  });
});

We called createSpyObj with the spy name as the first argument.

And the methods available in the spy object in the 2nd argument.

Then we can it and then check if the methods are defined

Matching and Spies

We can add more checks with more matches.

For instance, we can write:

describe("Tests", function () {
  it("matches any value", function () {
    expect({}).toEqual(jasmine.any(Object));
    expect(12).toEqual(jasmine.any(Number));
  });

  describe("when used with a spy", function () {
    it("is useful for comparing arguments", function () {
      const foo = jasmine.createSpy('foo');
      foo(12, () => true);
      expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function));
    });
  });
});

We have can use toEqual to check the type of a value with jasmine.any .

We can use jasmine.any with toHaveBeenCalledWith to check if a spy is called with a given type of data.

jasmine.anything

We can use jasmine.anythibng to check if something matches anything that’s not null or undefined .

For instance, we can write:

describe("jasmine.anything", function () {
  it("matches anything", function () {
    expect(1).toEqual(jasmine.anything());
  });

  describe("when used with a spy", function () {
    it("is useful when the argument can be ignored", function () {
      const foo = jasmine.createSpy('foo');
      foo(12, 'bar');
      expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
    });
  });
});

We have jasmine.anything() call to check if anything isn’t null or undefined .

Checking Object Structure

We can check for object structure with the jasmine.objectContaining method.

For instance, we can write:

describe("jasmine.objectContaining", function () {
  let foo;

  beforeEach(function () {
    foo = {
      a: 1,
      b: 2,
      bar: "baz"
    };
  });

  it("matches objects with key-value pairs", function () {
    expect(foo).toEqual(jasmine.objectContaining({
      a: 1, bar: "baz"
    }));
  });

  describe("when used with a spy", function () {
    it("is useful for comparing arguments", function () {
      const callback = jasmine.createSpy('callback');

      callback({
        bar: "baz"
      });

      expect(callback)
        .toHaveBeenCalledWith(
          jasmine.objectContaining({
            bar: "baz"
          })
        );
    });
  });
});

We have the jasmine.objectContaining method to watch for object structure.

We just pass in the key-value pairs we want to check for.

It can be used with values and function arguments.

Conclusion

We can check for values and objects and watch for spies with various Jasmine methods.

Categories
Testing

Jasmine — Async Tests

Testing is an important part of JavaScript.

In this article, we’ll look at how to create more complex tests with Jasmine.

Testing Promises

We can test promises with Jasmine.

The beforeAll , afterAll , beforeEach and afterEach functions all support promises.

For instance, we can write:

describe("Using promises", function () {
  let value;

  beforeEach(function () {
    return Promise.resolve().then(function () {
      value = 0;
    });
  });

  it("should support async execution of tests", function () {
    return Promise.resolve().then(function () {
      value++;
      expect(value).toBeGreaterThan(0);
    });
  });
});

We called then on a promise in the beforeEach and it callbacks.

Then we can check the values in there.

It also works with the async and await syntax.

For instance, we can write:

describe("Using promises", function () {
  let value;

  beforeEach(async function () {
    await Promise.resolve()
    value = 0;
  });

  it("should support async execution of tests", async function () {
    await Promise.resolve()
    value++;
    expect(value).toBeGreaterThan(0);
  });
});

We just rewrote the promises with the async and await syntax.

In both examples, they’ll run sequentially.

The test starts with the beforeEach callback and then run the it callback.

Changing Wait Time

The beforeEach and afterEach functions take an optional 2nd argument to set the wait until done is finished.

For example, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function (done) {
    done();
  }, 1000);

  it("takes a long time", function (done) {
    setTimeout(function () {
      done();
    }, 9000);
  }, 10000);

  afterEach(function (done) {
    done();
  }, 1000);
});

to pass in a 2nd argument with the wait time in milliseconds.

Handling Failures

Handling failures with promises can be done like any other piece of code.

If a promise is rejected in our test, then the test will fail.

For instance, if we have:

describe("promise test", function () {
  it('does a thing', function () {
    return Promise.reject()
  });
});

then the test will fail.

With async and await , we get the same result:

describe("long asynchronous specs", function () {
  it('does a thing', async function () {
    await Promise.reject()
  });
});

This will also fail.

We can fail a test with callbacks.

Since Jasmine 3.0, the done function can detect if an Error object is passed to the function.

If it is, then the test will fail.

For instance, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function (done) {
    setTimeout(function () {
      let err;

      try {
        throw new Error()
      } catch (e) {
        err = e;
      }

      done(err);
    });
  });

  it('does something', () => { })
});

Our beforeEach callback throw an error.

So since we call done with the err object, we’ll see that the test will fail.

Mock Clock

We can use the mock clock to avoid writing async tests.

It’ll make async code run synchronously.

For instance, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function () {
    jasmine.clock().install();
  });

  afterEach(function () {
    jasmine.clock().uninstall();
  });

  it('does something after 10 seconds', function () {
    const callback = jasmine.createSpy('callback');
    setTimeout(function () {
      callback('foo');
    }, 10000);
    jasmine.clock().tick(10000);
    expect(callback).toHaveBeenCalledWith('foo');
  });
});

to create a test with the mock clock.

We called jasmine.clock().install() to create the clock.

And then we run the test code by calling tick to move to the time we want.

Then when the test is done, we call jasmine.clock().uninstall() to remove the mock clock.

Conclusion

We can test various kinds of async code with Jasmine.

Promises and callbacks are all supported.

And we can make timer code synchronous with a mock clock.

Categories
Testing

Jasmine — Arguments and Equality

Testing is an important part of JavaScript.

In this article, we’ll look at how to check for equality in different ways.

Custom Argument Matchers

We can create our own custom matches to check for any kind of data we want.

For instance, we can write:

function multipleOf(number) {
  return {
    asymmetricMatch(compareTo) {
      return compareTo % number === 0;
    },

    jasmineToString() {
      return `<multipleOf: ${number}>`;
    }
  };
}

describe("multipleOf tests", function () {
  it('calls Buffer.alloc with some that is a multiple of 1024', function () {
    spyOn(Buffer, 'alloc').and.callThrough();
    Buffer.alloc(2048);
    expect(Buffer.alloc).toHaveBeenCalledWith(multipleOf(1024));
  });
});

to create our matcher and our test that uses it.

The mutlipleOf function takes the number parameter, which is the argument we pass into the matcher.

In the function, we return an object with the asymmetricMatch and jasmineToString methods.

asymmetricMatch is what does the comparing.

We return true if the match is what we’re looking for and false otherwise.

jasmineToString lets us display the test name in a string.

Out matcher can be nested anywhere.

So we can write:

function multipleOf(number) {
  return {
    asymmetricMatch(compareTo) {
      return compareTo % number === 0;
    },

    jasmineToString() {
      return `<multipleOf: ${number}>`;
    }
  };
}

const foo = {
  bar() { }
}

describe("multipleOf tests", function () {
  it('calls foo.bar with some that is a multiple of 10', function () {
    spyOn(foo, 'bar');
    foo.bar({ name: 'mary', age: 20 });
    expect(foo.bar)
      .toHaveBeenCalledWith(
        {
          name: jasmine.any(String),
          age: multipleOf(10)
        }
      );
  });
});

We create a spy for foo.bar to watch for the parameters.

We can use our matcher with multipleOf like we do with the jasmine.any matcher.

Custom Equality Testers

We can create ou own custom equality tester with Jasmine.

For instance, we can write:

function customTester(first, second) {
  if (typeof first === 'string' && typeof second === 'string') {
    return first[0] === second[1];
  }
}

describe("multipleOf tests", function () {
  beforeEach(function () {
    jasmine.addCustomEqualityTester(customTester);
  });

  it('is equal using a custom tester', function () {
    expect('abc').toEqual(' a ');
  });

  it('is not equal using a custom tester', function () {
    expect('abc').not.toEqual('abc');
  });

  it('works in nested equality tests', function () {
    expect(['abc', '123']).toEqual([' a ', ' 1 ']);
  });
});

We create customTester that takes 2 parameters and check if 2 things are the same according to our criteria.

first is the value we’re checking and second is the value that we’re expecting.

We check if they’re both strings.

If they are, then we check if the first character of first is the same as the 2nd character of second .

It can check values no matter if they’re nested or not.

The last test does the check on each entry of the array.

Conclusion

We can check for matches in various ways.

We can check arguments and equality matches.

Categories
Testing

Jasmine — More Complex Tests

Testing is an important part of JavaScript.

In this article, we’ll look at how to create more complex tests with Jasmine.

Manually Failing a Spec With fail

We can call the fail function to manually fail a test.

For instance, we can write:

describe("A suite", function () {
  it("should fail", function () {
    fail('failing test');
  });
});

then we see that the test will fail.

Nesting describe Blocks

We can have nested describe blocks to let us describe more complex functionality.

Jasmine will walk down the tree to run the tests that are nested in the same order they’re written in.

beforeEach will run before each test.

And afterEach will run after each test.

For instance, we can write:

describe("A suite", function () {
  let foo;

  beforeEach(function () {
    foo = 1;
  });

  afterEach(function () {
    foo = 0;
  });

  it("foo is 1", function () {
    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function () {
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });

  describe("nested tests", function () {
    let bar;

    beforeEach(function () {
      bar = 1;
    });

    it("can reference both scopes as needed", function () {
      expect(foo).toEqual(bar);
    });
  });
});

We have a describe block that’s nested in another describe block.

The outer ones run first and then the inner ones will run once they’re reached by Jasmine.

Disabling Suites

We can disable suites by using xdescribe .

For instance, we can write:

xdescribe("A spec", function () {
  let foo;

  beforeEach(function () {
    foo += 1;
  });

  it("won't run", function () {
    expect(foo).toEqual(1);
  });
});

We have the xdescribe function to disable the test suite.

So the tests won’t run.

Pending Specs

We can use the xit function to disable a spec so that it won’t run.

For example, we can write:

describe("A spec", function () {
  xit("can be declared 'xit'", function () {
    expect(true).toBe(false);
  });

  it("has no expect");

  it("is pending", function () {
    expect(true).toBe(false);
    pending('this is why it is pending');
  });
});

We have the xit function to make the specs pending.

Also, we can make a spec pending with the pending function.

Spies

We can use spies to stub functions that we can use Jasmine to track calls and arguments.

For instance, we can write:

describe("A spy", function () {
  let foo, bar;
  beforeEach(function () {
    foo = {
      setBar(value) {
        bar = value;
      }
    };
    spyOn(foo, 'setBar');
    foo.setBar('foo');
    foo.setBar(1, 'bar');
  })

  it("tracks that the spy was called", function () {
    expect(foo.setBar).toHaveBeenCalled();
  });

  it("tracks that the spy was called x times", function () {
    expect(foo.setBar).toHaveBeenCalledTimes(2);
  });

  it("tracks all the arguments of its calls", function () {
    expect(foo.setBar).toHaveBeenCalledWith('foo');
    expect(foo.setBar).toHaveBeenCalledWith(1, 'bar');
  });

  it("stops all execution on a function", function () {
    expect(bar).toBeUndefined();
  });

  it("tracks if it was called at all", function () {
    foo.setBar();
    expect(foo.setBar.calls.any()).toEqual(true);
  });
});

We have the beforeEach hook with the foo object with the setBar method.

In the hook, we call the spyOn method to stub the setBar method and then watch how it’s called and the result.

We can use the toHaveBeenCalled matcher to check if the function is called.

Also, we can use toHaveBeenCalledTimes to check how many times it’s been called.

The toHaveBeenCalledWith method lets us check what parameters have the functions been called when they’re called.

calls.any() lets us check if a spied on function has been called.

Conclusion

We can make tests pending and fail with built-in functions.

And we can spy on functions to see how they’re called.

Categories
Testing

Jasmine — First Test Suite

Testing is an important part of JavaScript.

In this article, we’ll look at how to create simple tests with Jasmine.

The First Test

We can create our first test by creating a file in the spec folder.

The file should end with Spec.js .

Then in it, we can write:

describe("A suite", function() {
  it("should always pass", function() {
    expect(true).toBe(true);
  });
});

describe is a function to describe our test suite.

it is a function that runs a test.

The string has the description.

expect lets us check if a value is a value we expect.

This should always pass since true is always the same as true .

Then we can run that with npx jasmine .

The names will help us find the tests in a large test suite.

Specs are defined by calling the global Jasmine function/

It takes a string like, drscribe and it .

The string is the title of the spec.

Matchers

A matcher implements a boolean comparison between the actual value and the expected value.

It’s used for reporting to Jasmine if the expectation is true or false .

Then Jasmine can determine if the test passed or failed.

We have the toBe matcher in the example earlier to check if the value is the given value.

Setup and Teardown

To keep our test code from having repeated code, we can add setup and teardown functions.

We can pass in a callback to the beforeEach function to run code before each test.

And we can do the same with the afterEach function to run the code after each test.

beforeAll lets us run some code before running the suite.

And afterAll lets us run some code after running the suite.

For instance, we can write:

describe("A suite", function () {
  let foo = 0;

  beforeEach(function () {
    foo += 1;
  });

  afterEach(function () {
    foo = 0;
  });

  it("should set foo to 1", function () {
    expect(foo).toBe(1);
  });
});

to add the beforeEach and afterEach callbacks to run the code after each test.

We can do the same with beforeAll and afterAll :

describe("A suite", function () {
  let foo = 0;

  beforeAll(function () {
    foo = 1;
  });

  afterAll(function () {
    foo = 0;
  });

  it("should set foo to 1", function () {
    expect(foo).toBe(1);
  });
});

Using this

We can share variables between different callbacks with this .

this is accessible if we use traditional functions.

For instance, we can write:

describe("A suite", function () {
  beforeEach(function () {
    this.foo = 0;
  });

  it("can use this in foo", function () {
    expect(this.foo).toEqual(0);
    this.bar = "foobar";
  });

  it("prevents test pollution by having an empty this created for  the next spec", function () {
    expect(this.foo).toEqual(0);
    expect(this.bar).toBe(undefined);
  });
});

We have this.foo , which we set in the beforeEach callback.

Then it’ll be accessible within the specs.

However, if we set a new this value in a spec, it won’t be available to the other specs.

This prevents us from having tests that are dependent on each other.

If we have dependencies, then it’ll be a headache to sort them out.

Conclusion

Creating tests is simple with Jasmine.

We can eliminate repetition by using various hooks.