0
votes

Here's the code I would like to test. Specifically, I want to spy on a utility called Linkvalidation.validate to make sure that it is called when handleSave() is called.

This code lives in a component called the CondensedFormModal:

handleSave() {
  LinkValidation.validate(this.state.url)
  .then((response) => {
    if (response.success) {
      this.setState({
        message: ''
      });
    }
  })
  .fail((error) => {
    if (error && error.message && error.message.match(/invalid internal link/)) {
      this.setState({
        message: 'The URL is an internal link. Please use a public link.'
      });
    } else {
      this.setState({
        message: 'The URL is invalid.'
      });
    }
  });

Here is the LinkValidation.validate utility I'm using in the handleSave function above:

  define([
  'module/api-call'
  ], function(
  ApiCall
  ) {

  'use strict';

  // Calls validation API
  function validate(link) {
  var deferred = $.Deferred();

  ApiCall.apiCall(
    '/url/check',
    { link: link },
    'POST',
    function(data) {
      if (data.success === true) {
        deferred.resolve(data);
      } else {
        // This link is not valid
        deferred.reject(data);
      }
    },
    function() {
      deferred.reject();
    }
  );

  return deferred;
}

return {
  validate: validate
};
});

Here is my test file--

Import statement: import { validate } from 'modules/link-validation.js';

Test:

describe('when the URL is an internal link', () => {
          it('displays a unique error message', (done) => {
            let modal = shallowInstance(<CondensedFormModal />);
            modal.setState({
              url: 'https://internal.example.com'
            });

            let x = jasmine.createSpy('validate').and.returnValue({
              message: "invalid internal link",
              success: false,
              url: 'https://example.com'
            });

            modal.handleSave();
            _.defer(() => {
              expect(x).toHaveBeenCalled();
              done();
            });
          });
   });

When I run this test, it consistently fails with the message "Expected spy validate to have been called."

After looking at the Jasmine docs (https://jasmine.github.io/2.1/introduction) and various other Stack Overflow questions (Unit test with spy is failing. Says spy was never called , Jasmine test case error 'Spy to have been called' , etc.) I'm unable to make this work. I've also tried callFake and callThrough instead of returnValue.

Any ideas on how to spy on LinkValidation.validate to assure that it was called?

1

1 Answers

0
votes

This line:

let x = jasmine.createSpy('validate')

creates new spy function (it doesn't spy on existing validate function) and handleSave function is not aware of it. So it's not called at all. You have to set spy on function that is actually called in your component. Since your CondensedFormModal uses LinkValidation module (which I assume is imported in component file) you have to set spy on validate function from imported module which is actually used by component. So I'd suggest something like this:

  1. In CondensedFormModal constructor set LinkValidation as component property to make it easily accessible in tests:

    this.LinkValidation = LinkValidation;
    
  2. In handleSave use validate function like this:

    this.LinkValidation.validate(this.state.url);
    
  3. And finally in test set spy on component validate method:

    describe('when the URL is an internal link', () => {
          it('displays a unique error message', (done) => {
            let modal = shallowInstance(<CondensedFormModal />);
            ...
    
            spyOn(modal.LinkValidation, 'validate').and.returnValue({
              message: "invalid internal link",
              success: false,
              url: 'https://dash.vagrant.local.rf29.net/shopping/new'
            });
    
            modal.handleSave();
            _.defer(() => {
              expect(modal.LinkValidation.validate).toHaveBeenCalled();
              done();
            });
          });
     });