简体   繁体   中英

How to test function that requires user input in command line using mocha, chai, and robotjs?

I am trying to unit test this function with mocha, chai, and robotjs. I am currently able to get robotjs to input data, but I am not able to test this function by itself. Right now my terminal is saying that I expected {} to equal 'https://toscrape.com/'. 'https://toscrape.com/' is the baseurl used in this function, and if the user types no, this should be returned. An object is returned from another function later down the line. How do I test this function?

// allow user to input a url and then validate url
const requestSiteURL = async function () {
  let url = await new Promise((resolve) => {
    readline.question('Please type url: ', resolve);
  });
  let URL = 'https://toscrape.com/';
  if (validUrl.isUri(url)) {
    readline.close();
    return url;
  } else if (url.toLowerCase() === 'no') {
    url = URL;
    return url;
  } else {
    console.log(
      'Please type in a valid URL (https://toscrape.com/) or type "no" to use base url.'
    );
    return requestSiteURL();
  }
};

here is my test case so far


const url = require('../crawler');
const robot = require('robotjs');
var chai = require('chai');
var expect = chai.expect;

var roboInput = () => {
  robot.typeString('no');
  robot.keyTap('enter');
};

describe('validates site url', function () {
  it('no', function () {
    roboInput();
    let result = url.requestSiteURL();
    expect(result).to.equal('https://toscrape.com/');
  });
});

You want to write unit tests, so you need to test your code in an isolated, side-effect-free environment. In order to provide such an environment, it is necessary to create stubs or mocks for modules and methods with side effects. Here I will use sinon.js , because it is often used in conjunction with the mocha test framework and the chai assertion library.

What is an isolated environment? For your example, we don't need users or robotjs automation scripts to really enter text in the terminal.

For your case, we should create stubs for readline.question() , readline.close() etc..

Eg

crawler.js :

const rl = require('readline');

const validUrl = {
  isUri(url) {
    return url === 'https://toscrape.com/';
  },
};
const readline = rl.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const requestSiteURL = async function() {
  let url = await new Promise((resolve) => {
    readline.question('Please type url: ', resolve);
  });
  let URL = 'https://toscrape.com/';
  if (validUrl.isUri(url)) {
    readline.close();
    return url;
  } else if (url.toLowerCase() === 'no') {
    url = URL;
    return url;
  } else {
    console.log('Please type in a valid URL (https://toscrape.com/) or type "no" to use base url.');
    return requestSiteURL();
  }
};

module.exports = { requestSiteURL };

crawler.test.js :

const chai = require('chai');
const sinon = require('sinon');
const readline = require('readline');
const expect = chai.expect;

function resetModules() {
  delete require.cache[require.resolve('./crawler')];
}

describe('65298539', () => {
  beforeEach(() => {
    resetModules();
  });
  afterEach(() => {
    sinon.restore();
  });
  it('should return url if it is valid', async () => {
    const readlineInterfaceStub = {
      question: sinon.stub().callsFake((query, callback) => {
        callback('https://toscrape.com/');
      }),
      close: sinon.stub(),
    };
    sinon.stub(readline, 'createInterface').returns(readlineInterfaceStub);
    const url = require('./crawler');
    const actual = await url.requestSiteURL();
    expect(actual).to.be.eql('https://toscrape.com/');
    sinon.assert.calledWithExactly(readlineInterfaceStub.question, 'Please type url: ', sinon.match.func);
    sinon.assert.calledOnce(readlineInterfaceStub.close);
  });

  it('should set default url if user enter "no"', async () => {
    const readlineInterfaceStub = {
      question: sinon.stub().callsFake((query, callback) => {
        callback('No');
      }),
    };
    sinon.stub(readline, 'createInterface').returns(readlineInterfaceStub);
    const url = require('./crawler');
    const actual = await url.requestSiteURL();
    expect(actual).to.be.eql('https://toscrape.com/');
    sinon.assert.calledWithExactly(readlineInterfaceStub.question, 'Please type url: ', sinon.match.func);
  });

  it('should recursive call', async () => {
    let callCount = 0;
    const readlineInterfaceStub = {
      question: sinon.stub().callsFake((query, callback) => {
        if (callCount === 0) {
          callCount++;
          callback('');
        } else {
          callback('No');
        }
      }),
    };
    sinon.stub(readline, 'createInterface').returns(readlineInterfaceStub);
    sinon.spy(console, 'log');

    const url = require('./crawler');
    const actual = await url.requestSiteURL();
    expect(actual).to.be.eql('https://toscrape.com/');
    sinon.assert.calledWithExactly(readlineInterfaceStub.question, 'Please type url: ', sinon.match.func);
    sinon.assert.calledWithExactly(
      console.log,
      'Please type in a valid URL (https://toscrape.com/) or type "no" to use base url.',
    );
  });
});

unit test result:

  65298539
    ✓ should return url if it is valid (1516ms)
    ✓ should set default url if user enter "no"
Please type in a valid URL (https://toscrape.com/) or type "no" to use base url.
    ✓ should recursive call


  3 passing (2s)

------------|---------|----------|---------|---------|-------------------
File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------|---------|----------|---------|---------|-------------------
All files   |     100 |      100 |     100 |     100 |                   
 crawler.js |     100 |      100 |     100 |     100 |                   
------------|---------|----------|---------|---------|-------------------

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM