Front-End
Unit Testing

PARTE 2

WORKSHOP DE

Facundo Gaumet // Joel A. Villarreal Bertoldi

Córdoba, 4 de Mayo de 2017

¿Qué haremos?

   + COVERAGE      
+ ASYNC TESTS

¿Comenzamos?

¿Qué es la cobertura?

Supongamos que se nos presenta la siguiente situación:

Dos proyectos, uno con el 95% de cobertura con tests, otro con 45%. Pagan por error encontrado. ¿En cuál te gustaría trabajar?

 

¿Qué tipo de cobertura?

TESTS UNITARIOS

TESTS DE ACEPTACIÓN

TESTS DE INTEGRACIÓN

Muchas herramientas miden la cobertura de sentencias, que también pueden conocerse como:

Muchas herramientas miden la cobertura de sentencias, que también pueden conocerse como:

COBERTURA DE SEGMENTOS

COBERTURA DE MÚLTIPLES CONDICIONES

COBERTURA DE RAMAS


 var status = doSomething();
 if (status === FATAL_ERROR) {
   return 3;
 }
 // No errors, continue...

 var status = doSomething();
 if (status === FATAL_ERROR) {
   return 3;
 } else if (status === WARNING) {
   // Recover from error.
 }
 // No errors, continue...

Podemos dividir el código testeado en tres categorías:

Podemos dividir el código testeado en tres categorías:

RIESGO ALTO

RIESGO MEDIO

RIESGO BAJO

  class MyObject extends EventEmitter {
    doSomething() {
      // Alert we're going to something.
      this.emit('beforeDoSomething');
  
      // Effectively doing something.
      this.x = this.x; 
  
      // Alert we've done something.
      this.emit('afterDoSomething');
    }
  }

doSomething()

(call entry point)

event:beforeDoSomething

event:afterDoSomething

return;

this.x = this.x

¿Qué sucede en esta función?

¿Cómo probamos esto?

describe('MyObject', function() {
  before(function() {
    this.obj = new MyObject();
  });
  it('should trigger all events', 
    function() {
      this.obj.doSomething();
    }
  );
});

No sirve ya que no tiene expectativas.

describe('MyObject', function() {
  before(function() {
    this.obj = new MyObject();
  });
  it('should trigger all events', 
    function() {
      const result = this.obj.doSomething();
      expect(result).to.equal(true);
    }
  );
});

No sirve ya que doSomething() no tiene retorno.

Además, el retorno no tiene relación con los eventos.

VAMOS PASO A PASO

describe('MyObject', function() {
  before(function() {
    this.obj = new MyObject();
  });
  it(
  'should trigger "before" event', 
  function() {
    this.obj.on('beforeDoSomething', function() {
      expect('everything').to.be.ok;
    });
    this.obj.doSomething();
  });
});

Capturando un evento con listeners

  it('should trigger "before" event', 
  function(done) {
    myObject.on('beforeDoSomething', 
      function() {
        done();
      }
    );
    myObject.doSomething();
  });

Capturando un evento con listeners: CALLBACKS

doSomething()

(call entry point)

event:beforeDoSomething

event:afterDoSomething

return;

this.x = this.x

Callback Testing

done()

  it('should trigger all events in the cycle', 
  function(done) {
    let calledBefore = false;

    myObject.on('beforeDoSomething', 
      () => { calledBefore = true; });
    myObject.on('afterDoSomething',
      () => { if (calledBefore) { done(); } });

    myObject.doSomething();
  });

Patrón de múltiples eventos con un callback

doSomething()

(call entry point)

event:beforeDoSomething

event:afterDoSomething

return;

this.x = this.x

Callback Testing

calledBefore = true

calledBefore ? done() : Error

  it('should trigger all events in the cycle', 
  function() {
    const test = new Promise((resolve, reject) => {
      let before = false;
      myObject.on('beforeDoSomething', 
        () => { before = true });
      myObject.on('afterDoSomething',
        () => before ? resolve : reject);
    });

    return test()
      .then(() => ...)
      .catch(() => ...);
  });

Captura de eventos + Promise Pattern

doSomething()

(call entry point)

event:beforeDoSomething

event:afterDoSomething

return;

this.x = this.x

Promise-Based Testing

before = true

before ? resolve() : reject()

Captura de eventos + Async/Await Pattern

!

IMPLEMENTACIÓN
NO ESTABLE

Captura de eventos + Async/Await Pattern

class MyAsyncObject {
  getItem(id) {
    return request(`/items/${id}`);  
  }
}

Captura de eventos + Async/Await Pattern

it('should return an item with
    the given ID', async function() {
  const item = await myObj.getItem(1);
  expect(item.id).to.equal(1);
});

Es sencillo conseguir un 100% de cobertura, pero lo difícil es probarlo todo.

Las herramientas de cobertura son útiles, siempre y cuando las usemos para fomentar el pensamiento lógico, no reemplazarlo.

¿Preguntas?

¡Muchas gracias!