LanguagesJavaScriptTesting Function Arguments with Jasmine and JavaScript

Testing Function Arguments with Jasmine and JavaScript

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

JavaScript Tutorial

Unit testing is one of those topics that can induce eye rolling in certain circles. It is something that most developers do not really want to do, but do it anyway, due to pressure from their organization. I get it. There was a time where I thought that unit testing was little more than a waste of time. That was until I saw its many benefits first-hand. If you are reading this web development tutorial, you are probably already converted, so there is no need to butter you up. Instead, let’s get right into the purpose of this tutorial, which is to test if the correct arguments have been passed to a function or method.

Perhaps you never even realized that this could be done. Not only is it possible, but it is far easier to do than you might suspect. We will see how to set up the function you want to test as a spy and define an expectation to verify passed arguments using the popular jasmine testing library for JavaScript.

Looking to learn JavaScript in a class or online course? We have a list of the Top Online Courses to Learn JavaScript to help get you started.

A Typical Test Suite in JavaScript

Before writing unit tests, we need a function to test. We will keep things simple by having our function perform math operations without any help from external objects or functions. The sumOddNumbers() function accepts a number of 1 or more as its single input parameter, which it then uses as the upper range of odd values to add together. For example, if we pass it the number 10, it will add up all – and return all – odd numbers between it and 0, in descending order, (i.e. 9 + 7 + 5 + 3 + 1, or 25):

const onlyOdds = (num) => {
  let sum = 0;
  while (num >= 1){
    if(num % 2 === 1){
        sum += num;
    }
    num--;
  }
  return sum
}

//displays 25
console.log(sumOddNumbers(10));

We would then write some tests that verify that the function returns the correct sums for various inputs:

describe('sumOddNumbers', () => {
  it('is a function', () => {
    expect(typeof sumOddNumbers).toEqual('function');
  });

  it('returns a number', () => {
    let returnedValue = sumOddNumbers(6);
    expect(typeof returnedValue).toEqual('number');
  });

  it('returns the sum of all odd nums between the provided argument and 0', () => {
    let returnedValue = sumOddNumbers(10);
    expect(returnedValue).toEqual(9 + 7 + 5 + 3 + 1);
  });

  it('returns 0 if inputted argument is less than 1', () => {
    let returnedValue = sumOddNumbers(-5);
    expect(returnedValue).toEqual(0);
  });
});

Within an application, the sumOddNumbers() function could be called many times with many different values. Depending on the complexity of the application code, some function invocations may not be occurring when we think they are. To test that, jasmine provides spies. An integral part of unit testing, spies track calls to a function and all its arguments. In the next section, we will use a spy to test what arguments were passed to the sumOddNumbers() function.

The spyOn() and createSpy() Methods in JavaScript

Jasmine provides two methodologies for spying on functions. These include spyOn() and createSpy(). SpyOn() is the more simplistic of the two, and is useful for testing the “real” function code to see if it was invoked.

Consider a situation where sumOddNumbers() is only called from a method under specific circumstances, such as this one:

class Maths {
  constructor(injectedSumOddNumbers) {
    this.sumOddNumbers = injectedSumOddNumbers || sumOddNumbers;
  }
  
  someMethod(someFlag, num) {
    let result;
    if (someFlag === true) {
      result = this.sumOddNumbers(num);
    } else {
      //do something else...
    }
    return result;
  }
}

In order to test sumOddNumbers(), we would need to create a spy that we would then inject into the class that needs it, either using annotations or some other means. Finally, our test would set up the necessary conditions for invoking the sumOddNumbers() function and call the class method that calls it:

it("was called at least once", () => {  
  const spiedSumOddNumbers = jasmine.createSpy("SumOddNumbers spy"); 
  //inject the spied method via the constructor
  const maths = new Maths(spiedSumOddNumbers);
  maths.someMethod(true, 99); 
  expect(spiedSumOddNumbers).toHaveBeenCalled(); 
}); 

Read: Top Unit Testing Tools for Developers

Checking a Function Argument’s Type in Jasmine

One of the neat things about jasmine spies is that they can substitute a fake function for the one that your testing, which is tremendously useful for stubbing complex functions that access a lot of resources and/or external objects. Here’s a test that employs the two argument createSpy() method; it accepts a spy name as the first parameter for easier recognition in long test reports. The fake function has access to the arguments object, which we can then inspect to gain valuable information about the number of arguments passed and their types:

it('was called with a number', () => {
  const spiedSumOddNumbers = 
        jasmine.createSpy('sumOddNumbersSpy', 'sumOddNumbers')
        .and.callFake(function() {
    expect(arguments.length).toEqual(1);
    expect(typeof arguments[0]).toEqual('number');
    return 0;
  });
  const maths = new Maths(spiedSumOddNumbers);
  maths.someMethod(true, 10); 
});

If you would rather employ an arrow function to define your fake function, you can ask the spy what types of parameters it received after the fact by calling toHaveBeenCalledWith(). It accepts a variable number of jasmine matchers that can accommodate most basic data types:

it('was called with a number', () => {
    const spiedSumOddNumbers = 
          jasmine.createSpy('sumOddNumbersSpy', 'sumOddNumbers')
          .and.callFake(() => 0);
    const maths = new Maths(spiedSumOddNumbers);
    maths.someMethod(true, 10); 
    
    expect(spiedSumOddNumbers).toHaveBeenCalledWith(
      jasmine.any(Number)
    );
  });

Verifying Function Arguments on Successive Invocations

Spies keep track of all invocations, so we can dig into the circumstances of each, including what parameters were passed to it. Everything we want to learn about successive invocations can be readily accessed via the calls namespace. It provides a number of helpful methods, a couple of which pertain to arguments. One of these is the allArgs() method. As the name suggests, it returns all the arguments passed to the spy, as a multi-dimensional array, whereby the first dimension stores the invocations, and the second holds all the parameters that were passed for that given invocation.

Here’s a test that checks the parameters of the sumOddNumbers() function over several invocations of maths.someMethod(). Of these, only three cause sumOddNumbers() to be invoked. Hence our test verifies both how many times the spy was called and with what arguments:

it('was called with specific numbers on successive calls', () => {
  const spiedSumOddNumbers = 
        jasmine.createSpy('sumOddNumbersSpy', 'sumOddNumbers')
        .and.callFake(() => 0);
  const maths = new Maths(spiedSumOddNumbers);
  maths.someMethod(true, 10); 
  maths.someMethod(false, 60);
  maths.someMethod(true, 60);
  maths.someMethod(true, 99);
  
  expect(spiedSumOddNumbers.calls.allArgs()).toEqual([
    [10],
    [60],
    [99]
  ]); 
});

You’ll find a demo of the above code on codepen.

Final Thoughts on Testing Function Arguments with Jasmine

In this JavaScript tutorial, we saw how easy it is to set up the method you want to test as a spy and define expectations to verify passed arguments using jasmine‘s createSpy() method. Spies can do a lot of other stuff that we didn’t cover here, so I would urge you to check out the official docs to get the whole picture.

Read more JavaScript programming tutorials and web development guides.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories