Let's test!!!

Typescript + Test + 驴GraphQL?

驴Qui茅n soy?

  • Full Stack Developer en Lean Mind
  • Apasionado del desarrollo
  • Typescript lover 馃挋

Adri谩n Ferrera Gonz谩lez

@afergon

adrian-afergon

驴De qu茅 vamos a hablar?

Como testear una aplicaci贸n de manera simple

驴Por qu茅 hacer tests?

No tengo test

驴Por d贸nde empiezo?

  • E2E
  • Unitarios
  • Integraci贸n

Si es un nuevo elemento

Si ya los elementos est谩n preparados

Si no conozco nada

E2E

  • Son caros respecto a tiempo
  • Cubren los casos de uso de forma humana
  • Se necesita un entorno real

Caracter铆sticas

驴C贸mo?

  • Docker
  • Servicio desplegada
  • Puppeteer
  • Jest

E2E

import puppeteer from 'puppeteer';

describe('Display video', () => {

  let browser: puppeteer.Browser;
  let page: puppeteer.Page;

  beforeEach(async () => {
    browser = await puppeteer.launch({
      headless: false,
    });
    page = await browser.newPage();

    await page.emulate({
      viewport: {
        width: 500,
        height: 2400,
      },
      userAgent: '',
    });

    await page.goto('http://localhost:3000/best-builds');
  });
  afterEach(async () => {
    await browser.close();
  });

  it('should select a video and view details ', async () => {
    await page.waitForSelector('[data-testid="video-item-1"]');
    const button = await page.$('[data-testid="video-item-1"]');
    if (button) {
      await button.evaluate((element: Element) => element.click());
    }

    const html = await page.$eval('[data-testid="video-details-title-1"]', (e: any) => e.innerHTML);
    expect(html).toBe('La amiga imaginaria y otros relatos de terror');
  });
});

E2E

  • Enterarnos si rompemos la aplicaci贸n
  • Tener un andamio para mover la estructura a una que se pueda probar
  • Garantizar que los casos de uso pedidos se cumplen

La finalidad

Unitarios

  • Son r谩pidos de ejecutar y desarrollar
  • Cubren abstracciones de c贸digo
  • Deber铆an de probarse de forma aislada

Caracter铆sticas

驴C贸mo?

  • Jest

Unitarios

// Test

describe('Videos', () => {
  it('should return the specified number of videos', async () => {
    const expectedQuantity = 4;
    const videosRepository: VideosRepository = new VideosRepository();

    const foundVideos: Video[] = await videosRepository.getVideos(expectedQuantity);

    expect(foundVideos.length).toBe(expectedQuantity);
  });
});
// Component

export VideosRepository {
  // ...
    
  public getVideos(quantity: number): Promise<Video[]> {
    return Promise.resolve(videos.slice(0, quantity));
  }
  
}

Integraci贸n

  • Son r谩pidos de ejecutar y desarrollar
  • Cubren un conjunto de funcionalidades
  • Hay que falsear llamadas (mocks)

Caracter铆sticas

驴C贸mo?

  • Jest
  • Arquitectura

Integraci贸n

隆驴Arquitectura?!

Integraci贸n

Arquitectura

Acceso a datos

C谩lculos y operaciones

Representaci贸n

Repository

Service

View

mapper

mapper

Integraci贸n

Arquitectura

Repository

Service

View

1. Vamos a consumir los datos del servicio

Por lo que omitiremos la capa de presentaci贸n

Integraci贸n

Arquitectura

Repository

Service

1. Vamos a consumir los datos del servicio

describe('Videos service', () => {
  it('should retrive the requested videos', async () => {
  	
    const allVideos = [ /* ? */ ];
  
    const videoService = new VideoService();
    const foundVideos = await videoService.find();
    
    expect(foundVideos).toBe(allVideos);
  
  });
});

2. Identificamos las dependencias y las inyectamos

describe('Videos service', () => {
  it('should retrive the requested videos', async () => {
  	
    const allVideos = [ terrorVideo, actionVideo ];
    const videoRepository = new VideoRepository();
  
    const videoService = new VideoService(videoRepository);
    const foundVideos = await videoService.find();
    
    expect(foundVideos).toBe(allVideos);
  
  });
});

Repository

Service

Integraci贸n

Arquitectura

3. Reemplazamos la llamada real por una propia

describe('Videos service', () => {
  it('should retrive the requested videos', async () => {
  	
    const allVideos = [ terrorVideo, actionVideo ];
    const videoRepository = new VideoRepository();
    videoRepository.getVideos = jest.fn( () => Promise.resolve(allVideos) );
    const videoService = new VideoService(videoRepository);
    const foundVideos = await videoService.find();
    
    expect(foundVideos).toBe(allVideos);
  
  });
});

Repository

Service

Integraci贸n

Arquitectura

export class VideoService {

  constructor() {
    this.videoRepository = new VideoRepository();
  }
  
  public async find(): Video[] {
  	return videoRepository.getVideos();
  }

}

驴Entonces tengo que cambiar todas las llamadas?

Repository

Service

Integraci贸n

Arquitectura

export class VideoService {

  constructor(
    private videoRepository = new VideoRepository()
  ) {}
  
  public async find(): Video[] {
  	return videoRepository.getVideos();
  }

}

No, solo le daremos un valor por defecto

Arquitectura

Repository

Service

Integraci贸n

Integraci贸n

Ventajas

  • Probamos gran parte de nuestro c贸digo
  • Invertimos poco tiempo en su desarrollo
  • Su ejecuci贸n es ligera

Desventajas

  • Hemos dejado reflejada informaci贸n en el test
  • Estos test requieren mantenimiento.
  • No son completamente reales

驴Qu茅 testear?

  • Tipos simples
  • Librer铆as de terceros
  • Tipos complejos
  • Mappers

Si

No

Questions?

Thank you!!

Let's test!!!

By afergon

Let's test!!!

This is a simple presentation about how we can test our typescript application

  • 239