CUM SA TESTEZI CODUL JAVASCRIPT?

jasmine

1. Introducere
Jasmine este un framework pentru testarea codului de JavaScript, care nu depinde de nici un alt framework de JavaScript. Contine o sintaxa curata, astfel incat testele pot fi scrise cu usurinta.

2. “Describe
Orice test incepe cu un call la functia Jasmine globala describe care are doi parametrii:

  • un string (numele sau titlu a ceea ce se testeaza);
  • o functie : (un bloc de cod prin care se implementeaza testul propriu-zis);

Acesta functie poate contine mai multe spec-uri.

3. “Specs”
Specificatiile testului sunt definite prin functia Jasmine globala it care are si ea doi parametrii:

  • un string (titlu specificatiilor);
  • o functie (testul);

Un spec (test) contine una sau mai multe “asteptari” care trebuie indeplinite, caz in care testul va fi executat cu succes. Aceste asa zise “asteptari” sunt construite cu functia expect care ia o valoare si apeleaza actuala valoare:

describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});

Astfel se verifica daca o anumita parte din cod face exact ceea ce ar trebui sa faca. Daca toate verficarile vor returna un rezultat true atunci testul va fi unul cu succes.

describe("A suite is just a function", function() {
  var x;

  it("and so is a spec", function() {
    x = true;

    expect(x).toBe(true);
  });
});

4. Variabile
Un aspect extrem de important este faptul ca variabilele declarate in describe sunt globale si vor fi accesibile in orice it.

5. “Expect”
Cu ajutorul functie expect se verifica daca o variabila are atribuita valoarea pe care o dorim sau daca o expresie, o functie funtioneaza corect.

6. “Matchers”
Exista numeroare functii pe care le putem folosi pentru a testa daca o valoaare este egala cu alta sau daca este mai mica, mai mare, daca este definita sau nu, sau daca un sir contine un anumit numar etc. Acestia se numesc matchers.

  • toBe
  • not.toBe
describe("Included matchers:", function() {

  it("The 'toBe' matcher compares with ===", function() {
    var x = 1;
    var y = x;

    expect(x).toBe(y);
    expect(x).not.toBe(null);
  });

Se poate observa ca orice matcher poate fi negat punand not la inceput.

  • toEqual
  • not.toEqual
  describe("The 'toEqual' matcher", function() {

    it("works for simple literals and variables", function() {
      var x = 1;
      expect(x).toEqual(1);
    });

    it("should work for objects", function() {
      var obj1 = {
        x: 1,
        y: 3
      };
      var obj2 = {
        x: 1,
        y: 3
      };
      expect(obj1).toEqual(obj2);
    });
  });

Se poate folosi atat pentru variabile cat si pentru obiecte.

  • toMatch
  • not.toMatch
  it("The 'toMatch' matcher is for regular expressions", function() {
    var message = "water milk banana";

    expect(message).toMatch(/milk/);
    expect(message).toMatch("milk");
    expect(message).not.toMatch(/dfx/);
  });

Aici se foloseste pentru a verifica daca respectiva propozitie contine acel cuvantul milk.

  • toBeDefined
  • toBeUndefined
  it("The 'toBeDefined' matcher compares against `undefined`", function() {
    var animal = {
      dog: "dog"
    };

    expect(animal.dog).toBeDefined();
    expect(animal.cat).not.toBeDefined();
  });

  it("The `toBeUndefined` matcher compares against `undefined`", function() {
    var animal = {
      dog: "dog"
    };

    expect(animal.dog).not.toBeUndefined();
    expect(animal.cat).toBeUndefined();
  });
  • toBeNull
  • not.toBeNull
  it("The 'toBeNull' matcher compares against null", function() {
    var x = null;
    var y = "y";

    expect(null).toBeNull();
    expect(x).toBeNull();
    expect(y).not.toBeNull();
  });
  • toBeTruthy
  • not.toBeTruthy
  it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
    var x, y = "y";

    expect(y).toBeTruthy();
    expect(x).not.toBeTruthy();
  });

Se verifica daca o variabila are sau nu valoare, iar in acest caz x este fara valoare.

  • toBeFalsy
  • not.toBeFalsy
  it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
    var x, y = "y";

    expect(x).toBeFalsy();
    expect(y).not.toBeFalsy();
  });
  • toContain
  • not.toContain
  describe("The 'toContain' matcher", function() {
    it("works for finding an item in an Array", function() {
      var a = ["milk", "water", "banana"];

      expect(a).toContain("banana");
      expect(a).not.toContain("uxas");
    });

    it("also works for finding a substring", function() {
      var a = "water banana milk";

      expect(a).toContain("banana");
      expect(a).not.toContain("uxas");
    });
  });
  • toBeLessThan
  it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
    var a = 3.9,
      b = 2.78;

    expect(b).toBeLessThan(a);
    expect(a).not.toBeLessThan(b);
  });
  • toBeGraterThan
  it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
    var a = 3.9,
      b = 2.78;

    expect(a).toBeGreaterThan(b);
    expect(b).not.toBeGreaterThan(a);
  });
  • toBeCloseTo
  it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
    var pi = 3.1415926,
      e = 2.78;

    expect(pi).not.toBeCloseTo(e, 2);
    expect(pi).toBeCloseTo(e, 0);
  });
  • toThrow
  it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
    var a = function() {
      return 1 + 5;
    };
    var b = function() {
      return a + 5;
    };
    var c = function() {
      throw 'error';
    };

    expect(a).not.toThrow();
    expect(b).toThrow();
    expect(c).toThrow('error');
  });
  • toThrowError
  it("The 'toThrowError' matcher is for testing a specific thrown exception", function() {
    var myFunction = function() {
      throw new TypeError("a b c");
    };

    expect(myFunction).toThrowError("a b c");
    expect(myFunction).toThrowError(/b/);
    expect(myFunction).toThrowError(TypeError);
    expect(myFunction).toThrowError(TypeError, "a b c");
  });
});

7. BeforeEach / AfterEach

Acestea se folosesc pentru a impiedica duplicarea codului. BeforeEach este chemat inaintea fiecarui spec, iar AfterEach dupa fiecare spec.
Variabilele vor fi declarate inainte de BeforeEach.In BeforeEach se asigneaza valori variabilelor deja declarate, iar in AfterEach se reseteaza valorile.

describe("A spec (with setup and tear-down)", function() {
  var a;

  beforeEach(function() {
    a = 0;
    a += 1;
  });

  afterEach(function() {
    a = 0;
  });

  it("is just a function, so it can contain any code", function() {
    expect(a).toEqual(1);
  });

  it("can have more than one expectation", function() {
    expect(a).toEqual(1);
    expect(true).toEqual(true);
  });
});

8. “this

Este un alt mod de a share-ui variabile intre BeforeEach, it si AfterEach. Nu vom mai aveam cuvantul “var”. Foarte important este faptul ca o variabila definita in it nu este accesibila in alt it, insa putem avea describe in describe.

describe("A spec", function() {
  beforeEach(function() {
    this.a = 0;
  });

  it("can use the `this` to share state", function() {
    expect(this.a).toEqual(0);
    this.b = "test";
  });

  it("a variable defined in the last it will not be accessible in this it", function() {
    expect(this.a).toEqual(0);
    expect(this.b).toBe(undefined);
  });
});

9. “xdescribe”

Cu xdescribe un test se anuleaza si nu va mai fi rulat.

10. Pending Specs

Aceste teste nu vor fi rulate (“xit”), dar numele le va aparea la rezultat. Orice spec fara body va aparea la fel.

11. “Spies”

Jasmine are functii de test duble numite “spies”. Un spy exista doar in blocul describe sau it unde este definit si va fi inlaturat dupa fiecare spec. Exista matchers speciali pentru interactiuna cu spies:

  • toHaveBeenCalled (retuneaza true daca spy-ul a fost chemat)
  • toHaveBeenCalledWith (retuneaza true daca spy-ul a fost chemat cu o anumita valoare)
describe("A spy", function() {
  var a, b = null;

  beforeEach(function() {
    a = {
      setValue: function(value) {
        b = value;
      }
    };

    spyOn(a, 'setValue');

    a.setValue(123);
    a.setValue(45, 'another param');
  });

  it("tracks that the spy was called", function() {
    expect(a.setValue).toHaveBeenCalled();
  });

  it("tracks all the arguments of its calls", function() {
    expect(a.setValue).toHaveBeenCalledWith(123);
    expect(a.setValue).toHaveBeenCalledWith(45, 'another param');
  });

  it("stops all execution on a function", function() {
    expect(b).toBeNull();
  });
});
  • and.callThrough

Folosind and.callThrough spy-ul va urmari toate call-urile.

describe("A spy, when configured to call through", function() {
  var a, b, c;

  beforeEach(function() {
    a = {
      setValue: function(value) {
        b = value;
      },
      getValue: function() {
        return b;
      }
    };

    spyOn(a, 'getValue').and.callThrough();

    a.setValue(123);
    c = a.getValue();
  });

  it("tracks that the spy was called", function() {
    expect(a.getValue).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(b).toEqual(123);
  });

  it("when called returns the requested value", function() {
    expect(c).toEqual(123);
  });
});
  • and.returnValue

Toate call-urile la functie vor returna o valoare specifica.

describe("A spy, when configured to fake a return value", function() {
  var a, b, c;

  beforeEach(function() {
    a = {
      setValue: function(value) {
        b= value;
      },
      getValue: function() {
        return b;
      }
    };

    spyOn(a, "getValue").and.returnValue(745);

    a.setValue(123);
    c = foo.getValue();
  });

  it("tracks that the spy was called", function() {
    expect(a.getValue).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(b).toEqual(123);
  });

  it("when called returns the requested value", function() {
    expect(c).toEqual(745);
  });
});
  • and.callFake
describe("A spy, when configured with an alternate implementation", function() {
  var a, b, c;
  beforeEach(function() {
    a = {
      setValue: function(value) {
        b = value;
      },
      getValue: function() {
        return b;
      }
    };
  spyOn(a, "getValue").and.callFake(function(arguments, can, be, received) {
      return 1001;
    });

    a.setValue(123);
    c = a.getValue();
  });

  it("tracks that the spy was called", function() {
    expect(a.getValue).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(b).toEqual(123);
  });

  it("when called returns the requested value", function() {
    expect(c).toEqual(1001);
  });
});
  • and.throwError

Toate call-urile la spy vor arunca o eroare.

describe("A spy, when configured to throw an error", function() {
  var a, b;

  beforeEach(function() {
    a = {
      setValue: function(value) {
        b = value;
      }
    };

    spyOn(a, "setValue").and.throwError("error");
  });

  it("throws the value", function() {
    expect(function() {
      a.setValue(123)
    }).toThrowError("error");
  });
});
  • and.stub

Comportamentul original la stub-ului poate fi retunat oricand se foloseste aceasta comanda.

describe("A spy", function() {
  var a, b = null;

  beforeEach(function() {
    a = {
      setValue: function(value) {
        b = value;
      }
    };

    spyOn(a, 'setValue').and.callThrough();
  });

  it("can call through and then stub in the same spec", function() {
    a.setBar(123);
    expect(b).toEqual(123);

    a.setValue.and.stub();
    b = null;

    a.setValue(123);
    expect(b).toBe(null);
  });
});
  • proprietatea “calls”

Fiecare call la un spy poate fi accesibil folosind proprietatea calls.

describe("A spy", function() {
  var foo, bar = null;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, 'setBar');
  });
it("tracks if it was called at all", function() {
    expect(foo.setBar.calls.any()).toEqual(false);

    foo.setBar();

    expect(foo.setBar.calls.any()).toEqual(true);
  });
it("tracks the number of times it was called", function() {
    expect(foo.setBar.calls.count()).toEqual(0);

    foo.setBar();
    foo.setBar();

    expect(foo.setBar.calls.count()).toEqual(2);
  });
it("tracks the arguments of each call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.argsFor(0)).toEqual([123]);
    expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]);
  });
it("tracks the arguments of all calls", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]);
  });
it("can provide the context and arguments to all calls", function() {
    foo.setBar(123);

    expect(foo.setBar.calls.all()).toEqual([{object: foo, args: [123]}]);
  });
it("has a shortcut to the most recent call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.mostRecent()).toEqual({object: foo, args: [456, "baz"]});
  });
it("has a shortcut to the first call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.first()).toEqual({object: foo, args: [123]});
  });
it("tracks the context", function() {
    var spy = jasmine.createSpy('spy');
    var baz = {
      fn: spy
    };
    var quux = {
      fn: spy
    };
    baz.fn(123);
    quux.fn(456);

    expect(spy.calls.first().object).toBe(baz);
    expect(spy.calls.mostRecent().object).toBe(quux);
  });
it("can be reset", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.any()).toBe(true);

    foo.setBar.calls.reset();

    expect(foo.setBar.calls.any()).toBe(false);
  });
});

If we need to trigger an observableArray mutation when one of its element changes. we can use valueHasMutated() function.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s