Sinon

teisiti (fr)

Kuidas testida, et funtsioon välja kutsumist? Funktsioon tuleb välja kutsuda isoleeritult, et testida just kindlat funtsionaalsust.

Võtame aluskes järgneva objekti, mille dropClass funtsiooni välja kutsume:

student = {
  dropClass: function(classId, cb) {
    //do stuff
    cb();
  }
};

Võib kirjutada funtsiooni, mis jäljendab tegelikku funtsiooni ja muuta seal jälgimisparameetrit:

describe('testing function calls', () => {
  it("should call the callback", () => {
      let called = false;
      function callback() {
        called = true;
      }

      student.dropClass(1, callback);
      called.should.be.true;
    });
});

Kuid see nõuab palju käsitööd ja saame hoopis kasutada funtsioonide jälgimiseks loodud eraldi raamistikku – sinon.

npm install sinon
const sinon = require('sinon');

Sinon on JavaScripti raamistik, mis testib funktsioonide välja kutsumist ja võimaldab kirjutada pettefunktsioone.

Spy

nuhk, spioon

Spy on funktsioon, mida sinon jälgib, ja oskab välja tuua: kas ja kui mitu korda on spy funtsiooni välja kutsutud ning mis parameetrid talle iga kord kaasa anti.

describe("student.dropClass", function() {
  it("should call the callback", () => {
    const spy = sinon.spy();

    student.dropClass(1, spy);
    spy.called.should.be.true;
  });
});

Spy võib jälgida ka teatud kindla funtsiooni välja kutsumist, kui anname sisendina kaasa funktsiooni, mida tahame jälgida:

it("should call the callback and log to the console", () => {
  function onClassDropped() {
    console.log("onClassDropped was called");
  }
  var spy = sinon.spy(onClassDropped);

  student.dropClass(1, spy);
  spy.called.should.be.true;
});

Stub

tükk, konts, nukk; välja juurima

Stub võimaldab jälgida tervet objekti. Kuna me tavaliselt töötame objektidega, mitte tavaliste funktsioonidega, siis stub-ide kasutamine on testimise käigus enam levinud.

describe.only("stubs", () => {
  const student = {
    dropClass: function(classId, cb) {
      //do stuff
      if (!!cb.dropClass) {
        cb.dropClass();
      } else {
        cb();
      }
    }
  };
  const schedule = {
    dropClass: function() {
      console.log("class dropped");
    }
  };

  it("should call a stubbed method", () => {...});
  });
});

Stub võimaldab testida objekti meetodite väljakutsumist:

it("shoud call a stubbed method", () => {
  const stub = sinon.stub(schedule);
  student.dropClass(1, stub.dropClass);
  stub.dropClass.called.should.be.true;
});

Viimase lahenduse oleks saanud lihtsalt ka spy-funktsiooniga lahendada. Kuid stubidel on palju lisavõimalusi – üks neist võimalustest on see, et me saame kontrollida, kuidas nad funktsioneerivad (muuta väärtust vastavalt oma vajadustele).

Näiteks testime lihtsat funktsiooni, mis tagastab alati tõese väärtuse:

const student = {
  addClass: function(schedule) {
    if(!schedule.classIsFull()) {
      return true;
    } else {
      return false;
    }
  }
};
const schedule = {
  classIsFull: function() {
    return true;
  }
};

Funtsiooni väärtusega saame mängida.

it("should return true when the class is not full", () => {
  const returnVal = student.addClass(schedule);
  returnVal.should.be.true;
});

Kuna praegu tagastab schedule.classIsFull() meetod alati tõese väärtuse, siis test ebaõnnestub, kuid stub võimaldab testis objekti meetodi tulemit muuta ka kasutada seda erinevate võimaluste läbi testimiseks:

it("should return true when the class is not full", () => {
  const stub = sinon.stub(schedule);
  stub.classIsFull.returns(false);

  const returnVal = student.addClass(stub);
  returnVal.should.be.true;
});

Stub-isid saab kontrollida palju paremini, kui spy-sid ja selle tõttu kasutatakse neid sagedamini.

Mock

mõnitama, pilkama; võlts, teeseldud

Mock erineb stubist või spyst, sest seda saab seadistada enne, kui kood käivitatakse. See tähendab, et saab seada üles eeldusi ja pärast testida, kas need peavad paika:

it("mocks schedule", () => {
  const mockObj = sinon.mock(schedule);
  const expectation = mockObj.expects("classIsFull").once();

  student.addClass(schedule);
  expectation.verify();
});

Mock võimaldab testida küllalt keerulisi stsenaariume ning enamikel juhtudel pole vaja mocki kasutada. Enamik kasutajaid saab oma testid tehtud kasutades stube.

Kogu kood:

"use strict";

const sinon = require("sinon");

describe("sinon tests", () => {
  let student, schedule;
  beforeEach(function() {
    student = {
      dropClass: function(classId, cb) {
        //do stuff
        if (!!cb.dropClass) {
          cb.dropClass();
        } else {
          cb();
        }
      },
      addClass: function(schedule) {
        if (!schedule.classIsFull()) {
          return true;
        } else {
          return false;
        }
      }
    };

    schedule = {
      dropClass: function() {
        console.log("class dropped");
      },
      classIsFull: function() {
        return true;
      }
    };
  });

  describe("sinon", function() {
    it("should call the callback", () => {
      const spy = sinon.spy();

      student.dropClass(1, spy);
      spy.called.should.be.true;
    });

    it("should call the callback and log to the console", () => {
      function onClassDropped() {
        console.log("onClassDropped was called");
      }
      var spy = sinon.spy(onClassDropped);

      student.dropClass(1, spy);
      spy.called.should.be.true;
    });

    it("should call the callback even if it's a method of an object", () => {
      sinon.spy(schedule, "dropClass");
      student.dropClass(1, schedule);
      schedule.dropClass.called.should.be.true;
    });
  });

  describe("stubs", () => {
    it("shoud call a stubbed method", () => {
      const stub = sinon.stub(schedule);
      student.dropClass(1, stub.dropClass);
      stub.dropClass.called.should.be.true;
    });

    it("should return true when the class is not full", () => {
      const stub = sinon.stub(schedule);
      stub.classIsFull.returns(false);

      const returnVal = student.addClass(stub);
      returnVal.should.be.true;
    });
  });

  describe("mocks", () => {
    it("mocks schedule", () => {
      const mockObj = sinon.mock(schedule);
      const expectation = mockObj.expects("classIsFull").once();

      student.addClass(schedule);
      expectation.verify();
    });
  });
});