Jest Test Runner

Testen in JavaScript

Zwei mögliche Stacks

Aufgabe Mocha Jest
Runner Mocha Jest
Reporter Mocha Jest
Code coverage Istanbul Jest
Specs Mocha Jest
Assertions Chai Jest
Mock & Spy Sinon Jest
Rendern & Navigieren im DOM Enzyme Enzyme

Testcases

  • test oder it
    test('it should work', () => {
      expect('...').toBe('...);
    });
    it('it should also work', () => {
      expect('...').toBe('...);
    });
  • Kann ein Promise für asynchrones testen zurückliefern:
    test('it should work', () => {
      return someFunctionReturningAPromise()
        .then( value => { expect(value).toBe('...') });
    });

Testsuites

  • Testsuites (optional, müssen nicht verwendet werden)
    describe('some feature', () => {
      test(() => {...});
      test(() => {...});
      test(() => {...});
    });
  • Überspringen (skip) / Auswählen (only) einzelner Tests:
    describe.only('some feature', () => { } );
    describe.skip('another feature', () => { });
    
    test.only('should work', () => { });
    test.skip('does not work, skip for now', () => { });
    
    

Lifecycle

  • Einrichten der Testumgebung/-daten und Mocks
  • Aufräumen nach den Tests
  • Können global oder innerhalb einer Testsuite definiert werden
  • Können Promises zurückliefern
beforeAll(() => {...});
beforeEach(() => {...});
afterEach(() => {...});
afterall(() => {...});
describe('...', () => {
  beforeEach(...);
  afterEach(...);
  ...
});

Expectations und Matcher

  • https://facebook.github.io/jest/docs/expect.html#content
  • expect() liefert eine Expectation zurück, auf der Matcher definiert sind:
    expect(actual).toXyz(expected);
    // for example:
    expect("Hello Jest").toBe("Hello Jest"); // => ok
    
  • Einige Matcher
    // Vergleich auf Identität
    expect(actual).toBe(expected);
    // Inhaltsvergleich:
    expect(actual).toEqual(expected);
    // true / false / null:
    expect(actual).toBeTruthy();
    expect(actual).toBeFalsy();
    expect(actual).toBeNull();
    // Länge (Array oder String)
    expect(actual).toHaveLength(123);
    

Snapshot testing

(Mit Enzyme)

import {shallow} from 'enzyme';
test('render correctly', () => {

  // 1. shallow render component with Enzyme...
  const component = shallow(<MyReactComponent />);

  // 2. snapshot matching:
  expect(component).toMatchSnapshot();
});

Rendert die Komponente in ein Snapshot-File und vergleicht damit bei folgenden Testausführungen

Snapshot sagt nicht, ob UI richtig oder falsch gerendert wird, sondern nur, ob sie geändert wurde!

Überblick: Snapshot testing

  • Bei erster Ausführung: legt Snapshot-File in __snapshot__ an (Beispiel)
  • Bei folgenden Ausführungen: erzeugt neuen Snapshot und vergleicht mit gespeichtertem Snapshot
  • Wenn Snapshots unterschiedlich
    • Fehler samt Diff (Beispiel)
    • Im Watch Mode kann Snapshot aktualisiert werden
  • Snapshotdateien werden in Git versioniert
  • Für Enzyme wird Serializer benötigt: enzyme-to-json

Mock Funktionen

  • jest.fn() erzeugt eine Mock-Funktion
    // Liefert undefined zurück, wenn ausgeführt
    const aMockFn = jest.fn();
    
    aMockFn(); // => undefined
                        
  • Implementierung der Mockfunktion kann als Parameter übergeben werden:
    
    const aMockFn = jest.fn( param => `Hello, ${param}` );
    
    console.log(aMockFn('World'));
    // => Hello, World
                    

Mock Funktionen

  • Aufrufe sicherstellen
    
    const aMockFn = jest.fn( param => `Hello, ${param}` );
    // use aMockFn somewhere in your code, then assert:
    
    expect(aMockFn).toHaveBeenCalled();
    expect(aMockFn).toHaveBeenCalledWith('World');
    
  • Aufrufe: fn.mock.calls
    
    const mockFn = jest.fn();
    mockFn('Hello', 'World');
    expect(mockFn.mock.calls).toHaveLength(1);
    expect(mockFn.mock.calls[0].toBe(['Hello', 'World']);
    
  • Zurücksetzen: Reset
    
    const mockFn = jest.fn();
    mockFn('Hello', 'World');
    expect(mockFn.mock.calls).toHaveLength(1);
    jest.restAllMocks(); // or: mockFn.mockReset();
    expect(mockFn.mock.calls).toHaveLength(0);
                
  • Mock modules

  • Ein Modul mocken:
    
    // external module:
    jest.mock('react-dom');
    // internal module:
    jest.mock('../../app/myDataService');
    
    // alle Funktionen sind jetzt gemockt,
    // "leere" Implementierung, return undefined)
    
  • Implementierung für Mockfunktion:
    
    jest.mock('../../app/myDataService', () => ({
      loadData: jest.fn( () => ({data: 'mock data'}) );
    });
    
    console.log(myDataService.loadData());
    // => {data: 'mock data'}
    
  • Mock modules

  • Funktionen aus gemockten Modulen sind Jest Mock Funktionen
    
    import myDataService from '../../app/myDataService';
    jest.mock('../../app/myDataService');
    
    // do something in code, that uses my dataservice
    expect(myDataService).toHaveBeenCalled();