5

Unit Testing Using Jasmine Spies

 2 years ago
source link: https://keyholesoftware.com/2021/12/13/unit-testing-using-jasmine-spies/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Unit Testing Using Jasmine Spies

A Jasmine spy can stub any function and track all calls to that function and all of its arguments. Jasmine Spies are a powerful tool to use in unit testing because they allow us to focus on the function that is being tested.

A spy will only exist in the describe or it block that it is defined. For this reason, when working on these tests, we will be using describe blocks per function that is being tested.

Let’s take a look at the Jasmine Spies I use most often when writing unit tests.

and.callThrough Spy

The first spy we will look at is the and.callThrough spy. This spy will track calls to the function it is spying on, but it will also pass through to the function and allow it to run. This means that when we use this spy, the function being spied on will still do what it is written to do, but we can track the calls to it. Look at the following example.

var stuff = [];
var thing = {
  addThing: function(value) {
    stuff.push(value);
  }
};

Now let’s install an and.callThrough spy for the addThing function of thing.

spyOn(thing, 'addThing').and.callThrough();

For all the spies in this article, we will be using spyOn(obj, methodName) to install our spies. For the spy on addThing we are saying that we want to install a spy on thing and that we are replacing the method with the method name addThing.

Now that the and.callThrough spy is installed, let’s look at what we can do with it by looking at some expect statements.

toHaveBeenCalled Matcher

If all we want to know is that our function that is being spied on has been called, toHaveBeenCalled is what we are looking for. The following expect just tests that our spy has been called, not caring about parameters, how many times it may have been called, or anything else.

thing.addThing('thing1');

expect(thing.addThing).toHaveBeenCalled();

When thing.addThing is called, the spy will register that it was called and this can now be used by the expect. The function will still do what it was going to do, so if we displayed the contents of stuff it would be ['thing1'].

toHaveBeenCalledTimes Matcher

To test that a function has been called the appropriate amount of times, use the toHaveBeenCalledTimes matcher.

spyOn(thing, 'addThing').and.callThrough();

thing.addThing('thing1');
thing.addThing('thing2');

expect(thing.addThing).toHaveBeenCalledTimes(2);

Because the spy is still and.callThrough, thing1 and thing2 will be added to stuff.

and.callFake Spy

Now let’s look at the and.callFake spy. This spy will track all calls to the defined function, but with this, we can pass in a new function that will be called in place of the original.

var calc = {
  add: function(x, y) {
    return x + y;
  }
}

spyOn(calc, 'add').and.callFake((x, y) => {return x + y + x});

expect(calc.add(2, 1)).toEqual(5);
expect(calc.add).toHaveBeenCalled();

and.callFake shines in its ability to allow you to define what a function call does depending on the describe or it block it is in. This spy is also very handy when a quick mock of a function is needed.

and.returnValue Spy

The and.returnValue spy is probably the one I have used most often. Just like the other spies, it will track the calls to the function, but instead of calling the function, you can specify a value to return. This can make setting up your test scenarios much easier.

var calc = {
  twoPlusTwo: function() {
    return 4;
  }
};

spyOn(calc, 'twoPlusTwo').and.returnValue(5);

expect(calc.twoPlusTwo()).toEqual(5);

and.throwError Spy

The last Jasmine Spy we will look at is the and.throwError spy. With this spy, every call will throw an error with the specified value.

var stuff = [];
var thing = {
  addThing: function(value) {
    stuff.push(value);
  }
};

spyOn(thing, 'addThing').and.throwError('ERROR');

expect(function() { thing.addThing('thing1') }).toThrowError('ERROR');
expect(function() { thing.addThing('thing2') }).toThrowError('ERROR');
expect(thing.addThing).toHaveBeenCalledTimes(2);

Using the and.throwError spy gives us an easy and clean way to have our tests throw an error, so we can make sure to test that our error functionality is also working as intended.

Conclusion

Unit testing is an important part of the development process. It gives us the ability to know that our code is working as intended and can also let us know if we may have broken something that we didn’t even think of.

With the help of Jasmine Spies, we can make our test setup easier, and we can give ourselves more options for what we can test against. Hopefully, this will give you a good starting point for all your Jasmine spying needs!

In case it would be helpful, I’ve attached my GitHub Repository. Take a look!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK