Jest kullanarak ES6 modül aktarımını nasıl alay edebilirim?


281

Bunun mümkün olmadığını düşünmeye başladım, ama yine de sormak istiyorum.

ES6 modüllerimden birinin belirli bir şekilde başka bir ES6 modülünü çağırdığını test etmek istiyorum. Yasemin ile bu çok kolay -

Uygulama kodu:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

Ve test kodu:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

Jest ile eşdeğer olan nedir? Bunun yapmak için çok basit bir şey olduğunu hissediyorum, ama saçımı anlamaya çalışırken yırtıyorum.

En yakın geldiğim s'leri imports ile değiştirmek requireve onları testlerin / fonksiyonların içinde hareket ettirmektir. İkisi de yapmak istediğim şeyler değil.

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Bonus puanlar için, içindeki işlev dependency.jsvarsayılan bir dışa aktarma olduğunda her şeyin çalışmasını isterim . Ancak, varsayılan ihracatta casusluğun Yasemin'de işe yaramadığını biliyorum (ya da en azından işe yarayamadım), bu yüzden Jest'te de mümkün olduğunu ummuyorum.


Bu proje için Babil'i zaten kullanıyorum, bu yüzden şimdilik s'yi imports'ye aktarmaya devam etmem umrumda değil require. Yine de dikkatleri için teşekkürler.
Cam Jackson

ts sınıfı A varsa ve bazı işlev çağırır diyelim ki B sınıfı doSomething () diyelim ki A sınıfı B sınıfı işlevinin alaycı sürümüne çağrı yapar doSomething ()
kailash yogeshwar

bu sorunu keşfetmek isteyenler için daha fazla github.com/facebook/jest/issues/936
omeralper

Yanıtlar:


221

Bunu içeren bir hack kullanarak çözebildim import *. Hem adlandırılmış hem de varsayılan ihracat için bile çalışır!

Adlandırılmış bir dışa aktarma için:

// dependency.js
export const doSomething = (y) => console.log(y)

// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Veya varsayılan bir dışa aktarma için:

// dependency.js
export default (y) => console.log(y)

// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

Mihai Damian'ın tam olarak aşağıda belirttiği gibi, bu modül nesnesini değiştiriyor dependencyve bu yüzden diğer testlere 'sızacak'. Bu nedenle, bu yaklaşımı kullanırsanız orijinal değeri saklamalı ve her testten sonra tekrar ayarlamalısınız. Bunu Jest ile kolayca yapmak için , orijinal değerinin kolayca geri yüklenmesini desteklediği için spyOn () yöntemini kullanın jest.fn(), bu nedenle belirtilen ' sızıntıdan önce kaçının .


Paylaşım için teşekkürler. Ben net sonuç buna benzer olduğunu düşünüyorum - ama bu daha temiz olabilir - stackoverflow.com/a/38414160/1882064
arcseldon

64
Bu işe yarıyor, ama muhtemelen iyi bir uygulama değil. Testin kapsamı dışındaki nesnelerde yapılan değişiklikler testler arasında devam ediyor gibi görünmektedir. Bu daha sonra diğer testlerde beklenmedik sonuçlara yol açabilir.
Mihai Damian

10
Jest.fn () kullanmak yerine, jest.spyOn () kullanabilirsiniz, böylece orijinal yöntemi daha sonra geri yükleyebilirsiniz, böylece diğer testlere akmaz. Burada farklı yaklaşımlar hakkında güzel bir makale buldum (jest.fn, jest.mock ve jest.spyOn): medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c .
Martinsos

2
Sadece bir not: dependencyile aynı dosyada bulunuyorsa myModule, çalışmaz.
Lu Tran

2
Bence mutasyona uğradığınız nesne salt okunur olan Typescript ile çalışmaz.
adredx

172

Modülü taklit etmeli ve casusu kendiniz ayarlamalısınız:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

4
Bu doğru görünmüyor. Ben olsun: babel-plugin-jest-hoist: The second argument of jest.mock must be a function.Yani kod bile derleme değil.
Cam Jackson

3
Üzgünüm, kodumu güncelledim. Lütfen yolun jest.mocktest dosyasına göre olduğunu da unutmayın .
Andreas Köberle

1
Ancak varsayılan ihracat kullanılırken bu benim için işe yaramadı.
Iris Schaffer

4
Bu işlevin varsayılan dışa aktarmayla çalışabilmesi __esModule: trueiçin, sahte nesneye eklemeniz gerekir . Bu, aktarılan bir es6 modülü veya ortak bir modül olup olmadığını belirlemek için aktarılan kod tarafından kullanılan dahili işarettir.
Johannes Lumpe

24
jest.mock('../dependency', () => ({ default: jest.fn() }))
Alay

50

Bir ES6 bağımlılık modülü varsayılan dışa aktarmasını jest kullanarak yapmak için:

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

Diğer seçenekler davam için işe yaramadı.


6
Sadece bir test için yapmak istersem bunu temizlemenin en iyi yolu nedir? içten sonra `` `afterEach (() => {jest.unmock (../ bağımlılık ');})` ``
nxmohamad

1
@falsarella doMock bu durumda gerçekten çalışıyor mu? Çok benzer bir sorun yaşıyorum ve belirli bir test içinde jest.doMock çalışırken hiçbir şey yapmaz, burada tüm modül için jest.mock düzgün çalışıyor
Progress1ve

1
@ Progress1ve sen de mockImplementationOnce ile jest.mock kullanmayı deneyebilirsiniz
falsarella

1
Evet, bu geçerli bir öneri, ancak testin birincisi olmasını gerektiriyor ve ben bu şekilde test yazma hayranı değilim. Harici modülü içe aktararak ve belirli işlevlerde spyOn kullanarak bu sorunları çözdüm.
İlerleme1ve

1
@ Progress1ve hmm Her bir özel testin içine mockImplementationOnce yerleştirmek istedim ... neyse, bir çözüm bulduğunuz için mutluyum :)
falsarella

38

Andreas yanıtına daha fazlasını ekliyoruz. ES6 kodu ile aynı sorunu yaşadım ama ithalatı mutasyona geçirmek istemedim. Keskin görünüyordu. Bu yüzden yaptım

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

Ve bağımlılık.js'ye paralel olarak "__ mocks __" klasörüne bağımlılık eklendi. Bu benim için çalıştı. Ayrıca, bu bana sahte uygulamadan uygun verileri döndürme seçeneği verdi. Alay etmek istediğiniz modüle doğru yolu verdiğinizden emin olun.


Bunun için teşekkürler. Bir deneyin. Bu çözümü de
beğendim

Bu yaklaşımla ilgili sevdiğim şey, size belirli bir modülü taklit etmek istediğiniz tüm durumlar için tek bir manuel alay imkanı sunmasıdır. Örneğin, birçok yerde kullanılan bir çeviri yardımcısı var. __mocks__/translations.jsDosya yalnızca ihracat varsayılan jest.fn()böyle bir şeye:export default jest.fn((id) => id)
İris Schaffer

jest.genMockFromModuleModüllerden alay oluşturmak için de kullanabilirsiniz . facebook.github.io/jest/docs/…
Varunkumar Nagarajan

2
Dikkat edilmesi gereken bir şey, alay edilen ES6 modüllerinin `` jest.mock ('.. bağımlılığı' ') çağrıldıktan sonra export default jest.genMockFromModule('../dependency')tüm işlevlerine atanmasıdır dependency.default, ancak aksi takdirde beklendiği gibi davranır.
jhk

7
Test iddianız neye benziyor? Bu cevabın önemli bir parçası gibi görünüyor. expect(???)
taş

14

2020'ye hızlı yönlendirme, bu bağlantıyı çözüm olarak buldum. yalnızca ES6 modülü sözdizimini kullanarak https://remarkablemark.org/blog/2018/06/28/jest-mock-default-named-export/

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};

// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

Ayrıca bilmeniz gereken bir şey (ki bu beni anlamak için biraz zaman aldı) test içinde jest.mock () diyemezsiniz; modülün en üst seviyesinde aramalısınız. Ancak, farklı testler için farklı alaylar ayarlamak istiyorsanız, ayrı ayrı testlerin içinde mockImplementation () yöntemini çağırabilirsiniz.


5

Soru zaten cevaplandı, ancak şu şekilde çözebilirsiniz:

dependency.js

const doSomething = (x) => x
export default doSomething;

myModule.js:

import doSomething from "./dependency";

export default (x) => doSomething(x * 2);

myModule.spec.js:

jest.mock('../dependency');
import doSomething from "../dependency";
import myModule from "../myModule";

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    doSomething.mockImplementation((x) => x * 10)

    myModule(2);

    expect(doSomething).toHaveBeenCalledWith(4);
    console.log(myModule(2)) // 40
  });
});

Ama "gereksinim" CommonJS sözdizimi - OP ES6 Modülleri hakkında soruyordu
Andy

@ Yorumunuz için teşekkürler, cevabımı güncelledim. BTW mantıkta aynı şey.
İnce

2

Bunu başka bir şekilde çözdüm. Diyelim ki bağımlılığınız var.

export const myFunction = () => { }

Aşağıdaki içerikle birlikte bir depdency.mock.js dosyası oluşturuyorum:

export const mockFunction = jest.fn();

jest.mock('dependency.js', () => ({ myFunction: mockFunction }));

ve testte, kullandığım bağımlılığa sahip dosyayı içe aktarmadan önce:

import { mockFunction } from 'dependency.mock'
import functionThatCallsDep from './tested-code'

it('my test', () => {
    mockFunction.returnValue(false);

    functionThatCallsDep();

    expect(mockFunction).toHaveBeenCalled();

})
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.