99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Jest ES6 類模擬

2021-09-18 20:26 更新

Jest 可用于模擬導入到要測試的文件中的 ES6 類。

ES6 類是帶有一些語法糖的構造函數(shù)。因此,任何 ES6 類的模擬都必須是一個函數(shù)或一個實際的 ES6 類(這又是另一個函數(shù))。所以你可以使用模擬函數(shù)來模擬它們。

ES6 類示例

我們將使用一個播放聲音文件的類的人為示例,?SoundPlayer?,以及使用該類的使用者類?SoundPlayerConsumer?。我們將?SoundPlayer?在我們的測試中模擬?SoundPlayerConsumer?.

  1. // sound-player.js
  2. export default class SoundPlayer {
  3. constructor() {
  4. this.foo = 'bar';
  5. }
  6. playSoundFile(fileName) {
  7. console.log('Playing sound file ' + fileName);
  8. }
  9. }
  1. // sound-player-consumer.js
  2. import SoundPlayer from './sound-player';
  3. export default class SoundPlayerConsumer {
  4. constructor() {
  5. this.soundPlayer = new SoundPlayer();
  6. }
  7. playSomethingCool() {
  8. const coolSoundFileName = 'song.mp3';
  9. this.soundPlayer.playSoundFile(coolSoundFileName);
  10. }
  11. }

創(chuàng)建 ES6 類模擬的 4 種方法

自動模擬

調用?jest.mock('./sound-player')?返回一個有用的“自動模擬”,你可以使用它來監(jiān)視對類構造函數(shù)及其所有方法的調用。它取代了ES6類與模擬構造,并將其所有方法始終返回未定義的模擬函數(shù)。方法調用保存在?theAutomaticMock.mock.instances[index].methodName.mock.calls?.

請注意,如果你在類中使用箭頭函數(shù),它們將不會成為模擬的一部分。原因是箭頭函數(shù)不存在于對象的原型中,它們只是持有對函數(shù)的引用的屬性。

如果不需要替換類的實現(xiàn),這是最容易設置的選項。例如:

  1. import SoundPlayer from './sound-player';
  2. import SoundPlayerConsumer from './sound-player-consumer';
  3. jest.mock('./sound-player'); // SoundPlayer is now a mock constructor
  4. beforeEach(() => {
  5. // Clear all instances and calls to constructor and all methods:
  6. SoundPlayer.mockClear();
  7. });
  8. it('We can check if the consumer called the class constructor', () => {
  9. const soundPlayerConsumer = new SoundPlayerConsumer();
  10. expect(SoundPlayer).toHaveBeenCalledTimes(1);
  11. });
  12. it('We can check if the consumer called a method on the class instance', () => {
  13. // Show that mockClear() is working:
  14. expect(SoundPlayer).not.toHaveBeenCalled();
  15. const soundPlayerConsumer = new SoundPlayerConsumer();
  16. // Constructor should have been called again:
  17. expect(SoundPlayer).toHaveBeenCalledTimes(1);
  18. const coolSoundFileName = 'song.mp3';
  19. soundPlayerConsumer.playSomethingCool();
  20. // mock.instances is available with automatic mocks:
  21. const mockSoundPlayerInstance = SoundPlayer.mock.instances[0];
  22. const mockPlaySoundFile = mockSoundPlayerInstance.playSoundFile;
  23. expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
  24. // Equivalent to above check:
  25. expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
  26. expect(mockPlaySoundFile).toHaveBeenCalledTimes(1);
  27. });

手動模擬

通過在?__mocks__?文件夾中保存模擬實現(xiàn)來創(chuàng)建手動模擬??。這允許指定實現(xiàn),并且它可以跨測試文件使用。

  1. // __mocks__/sound-player.js
  2. // Import this named export into your test file:
  3. export const mockPlaySoundFile = jest.fn();
  4. const mock = jest.fn().mockImplementation(() => {
  5. return {playSoundFile: mockPlaySoundFile};
  6. });
  7. export default mock;

導入所有實例共享的模擬和模擬方法:

  1. // sound-player-consumer.test.js
  2. import SoundPlayer, {mockPlaySoundFile} from './sound-player';
  3. import SoundPlayerConsumer from './sound-player-consumer';
  4. jest.mock('./sound-player'); // SoundPlayer is now a mock constructor
  5. beforeEach(() => {
  6. // Clear all instances and calls to constructor and all methods:
  7. SoundPlayer.mockClear();
  8. mockPlaySoundFile.mockClear();
  9. });
  10. it('We can check if the consumer called the class constructor', () => {
  11. const soundPlayerConsumer = new SoundPlayerConsumer();
  12. expect(SoundPlayer).toHaveBeenCalledTimes(1);
  13. });
  14. it('We can check if the consumer called a method on the class instance', () => {
  15. const soundPlayerConsumer = new SoundPlayerConsumer();
  16. const coolSoundFileName = 'song.mp3';
  17. soundPlayerConsumer.playSomethingCool();
  18. expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
  19. });

jest.mock() 使用模塊工廠參數(shù)調用

?jest.mock(path, moduleFactory)?接受一個模塊工廠參數(shù)。模塊工廠是一個返回模擬的函數(shù)。

為了模擬構造函數(shù),模塊工廠必須返回一個構造函數(shù)。換句話說,模塊工廠必須是一個返回函數(shù)的函數(shù)——高階函數(shù)(HOF)。

  1. import SoundPlayer from './sound-player';
  2. const mockPlaySoundFile = jest.fn();
  3. jest.mock('./sound-player', () => {
  4. return jest.fn().mockImplementation(() => {
  5. return {playSoundFile: mockPlaySoundFile};
  6. });
  7. });

factory 參數(shù)的一個限制是,因為調用?jest.mock()?被提升到文件的頂部,所以不可能先定義一個變量然后在工廠中使用它。以單詞“mock”開頭的變量是一個例外。由您來保證它們會按時初始化!例如,由于在變量聲明中使用了 'fake' 而不是 'mock',以下代碼將拋出一個范圍外錯誤:

  1. // Note: this will fail
  2. import SoundPlayer from './sound-player';
  3. const fakePlaySoundFile = jest.fn();
  4. jest.mock('./sound-player', () => {
  5. return jest.fn().mockImplementation(() => {
  6. return {playSoundFile: fakePlaySoundFile};
  7. });
  8. });

使用 mockImplementation() 或替換模擬 mockImplementationOnce()

可以通過對現(xiàn)有的模擬調用?mockImplementation()?來替換上述所有模擬,以更改單個測試或所有測試的實現(xiàn)。

對 jest.mock 的調用被提升到代碼的頂部??梢陨院笤?code style="font-size: 15px;">beforeAll()?指定一個模擬,方法時對現(xiàn)有模擬調用?mockImplementation()?(或?mockImplementationOnce()?), 而不是使用工廠參數(shù)。如果需要,這還允許在測試之間更改模擬:

  1. import SoundPlayer from './sound-player';
  2. import SoundPlayerConsumer from './sound-player-consumer';
  3. jest.mock('./sound-player');
  4. describe('When SoundPlayer throws an error', () => {
  5. beforeAll(() => {
  6. SoundPlayer.mockImplementation(() => {
  7. return {
  8. playSoundFile: () => {
  9. throw new Error('Test error');
  10. },
  11. };
  12. });
  13. });
  14. it('Should throw an error when calling playSomethingCool', () => {
  15. const soundPlayerConsumer = new SoundPlayerConsumer();
  16. expect(() => soundPlayerConsumer.playSomethingCool()).toThrow();
  17. });
  18. });

深入:理解模擬構造函數(shù)

使用?jest.fn().mockImplementation()?構建構造函數(shù)??模擬會使模擬看起來比實際更復雜。本節(jié)介紹了如何創(chuàng)建自己的模擬,來說明模擬的工作原理。

另一個 ES6 類的手動模擬

如果使用與?__mocks__文件?夾中的模擬類相同的文件名定義 ES6 類,它將用作模擬。這個類將用于代替真正的類。這允許你為類注入測試實現(xiàn),但不提供監(jiān)視調用的方法。

對于人為的示例,模擬可能如下所示:

  1. // __mocks__/sound-player.js
  2. export default class SoundPlayer {
  3. constructor() {
  4. console.log('Mock SoundPlayer: constructor was called');
  5. }
  6. playSoundFile() {
  7. console.log('Mock SoundPlayer: playSoundFile was called');
  8. }
  9. }

使用模塊工廠參數(shù)模擬

傳遞給的模塊工廠函數(shù)?jest.mock(path, moduleFactory)?可以是返回函數(shù)*的 HOF。這將允許調用?new?模擬。同樣,這允許你測試注入不同的行為,但不提供監(jiān)視調用的方法。

* 模塊工廠函數(shù)必須返回一個函數(shù)

為了模擬構造函數(shù),模塊工廠必須返回一個構造函數(shù)。換句話說,模塊工廠必須是一個返回函數(shù)的函數(shù)——高階函數(shù)(HOF)。

  1. jest.mock('./sound-player', () => {
  2. return function () {
  3. return {playSoundFile: () => {}};
  4. };
  5. });

注意:箭頭函數(shù)不起作用

請注意,模擬不能是箭頭函數(shù),因為newJavaScript 中不允許調用箭頭函數(shù)。所以這行不通:

  1. jest.mock('./sound-player', () => {
  2. return () => {
  3. // Does not work; arrow functions can't be called with new
  4. return {playSoundFile: () => {}};
  5. };
  6. });

這將拋出?TypeError: _soundPlayer2.default is not a constructor?,除非代碼被轉換為 ES5,例如通過?@babel/preset-env?. (ES5 沒有箭頭函數(shù)和類,所以兩者都將被轉換為普通函數(shù)。)

跟蹤使用情況(監(jiān)視模擬)

注入測試實現(xiàn)很有幫助,但您可能還想測試是否使用正確的參數(shù)調用了類構造函數(shù)和方法。

監(jiān)視構造函數(shù)

為了跟蹤對構造函數(shù)的調用,將 HOF 返回的函數(shù)替換為 Jest 模擬函數(shù)。用 來創(chuàng)建它jest.fn(),然后用 來指定它的實現(xiàn)?mockImplementation()?。

  1. import SoundPlayer from './sound-player';
  2. jest.mock('./sound-player', () => {
  3. // Works and lets you check for constructor calls:
  4. return jest.fn().mockImplementation(() => {
  5. return {playSoundFile: () => {}};
  6. });
  7. });

這將讓我們檢查模擬類的使用情況,使用?SoundPlayer.mock.calls?:?expect(SoundPlayer).toHaveBeenCalled();?或接近等效的:?expect(SoundPlayer.mock.calls.length).toEqual(1);?

模擬非默認類導出

如果類不是模塊的默認導出,那么您需要返回一個對象,其鍵與類導出名稱相同。

  1. import {SoundPlayer} from './sound-player';
  2. jest.mock('./sound-player', () => {
  3. // Works and lets you check for constructor calls:
  4. return {
  5. SoundPlayer: jest.fn().mockImplementation(() => {
  6. return {playSoundFile: () => {}};
  7. }),
  8. };
  9. });

監(jiān)視我們類的方法

我們的模擬類需要提供?playSoundFile?在我們的測試期間將被調用的任何成員函數(shù)(在示例中),否則我們將在調用不存在的函數(shù)時出錯。但是我們可能還想監(jiān)視對這些方法的調用,以確保使用預期的參數(shù)調用它們。

每次在測試期間調用模擬構造函數(shù)時,都會創(chuàng)建一個新對象。為了監(jiān)視所有這些對象中的方法調用,我們填充?playSoundFile?了另一個模擬函數(shù),并將對同一個模擬函數(shù)的引用存儲在我們的測試文件中,以便在測試期間可用。

  1. import SoundPlayer from './sound-player';
  2. const mockPlaySoundFile = jest.fn();
  3. jest.mock('./sound-player', () => {
  4. return jest.fn().mockImplementation(() => {
  5. return {playSoundFile: mockPlaySoundFile};
  6. // Now we can track calls to playSoundFile
  7. });
  8. });

與此等效的手動模擬將是:

  1. // __mocks__/sound-player.js
  2. // Import this named export into your test file
  3. export const mockPlaySoundFile = jest.fn();
  4. const mock = jest.fn().mockImplementation(() => {
  5. return {playSoundFile: mockPlaySoundFile};
  6. });
  7. export default mock;

用法類似于模塊工廠函數(shù),不同之處在于您可以省略 from 的第二個參數(shù)?jest.mock()?,并且你必須將模擬方法導入到你的測試文件中,因為它不再在那里定義。為此使用原始模塊路徑;不包括?__mocks__?.

測試之間的清理

為了清除對模擬構造函數(shù)及其方法的調用記錄,我們mockClear()在?beforeEach()?函數(shù)中調用:

  1. beforeEach(() => {
  2. SoundPlayer.mockClear();
  3. mockPlaySoundFile.mockClear();
  4. });

完整示例

這是一個完整的測試文件,它使用模塊工廠參數(shù)來?jest.mock?:

  1. // sound-player-consumer.test.js
  2. import SoundPlayerConsumer from './sound-player-consumer';
  3. import SoundPlayer from './sound-player';
  4. const mockPlaySoundFile = jest.fn();
  5. jest.mock('./sound-player', () => {
  6. return jest.fn().mockImplementation(() => {
  7. return {playSoundFile: mockPlaySoundFile};
  8. });
  9. });
  10. beforeEach(() => {
  11. SoundPlayer.mockClear();
  12. mockPlaySoundFile.mockClear();
  13. });
  14. it('The consumer should be able to call new() on SoundPlayer', () => {
  15. const soundPlayerConsumer = new SoundPlayerConsumer();
  16. // Ensure constructor created the object:
  17. expect(soundPlayerConsumer).toBeTruthy();
  18. });
  19. it('We can check if the consumer called the class constructor', () => {
  20. const soundPlayerConsumer = new SoundPlayerConsumer();
  21. expect(SoundPlayer).toHaveBeenCalledTimes(1);
  22. });
  23. it('We can check if the consumer called a method on the class instance', () => {
  24. const soundPlayerConsumer = new SoundPlayerConsumer();
  25. const coolSoundFileName = 'song.mp3';
  26. soundPlayerConsumer.playSomethingCool();
  27. expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
  28. });


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號