2016-09-22 16 views
14

Tôi tạo một dự án bằng cách sử dụng create-app-component, cấu hình một ứng dụng mới có các tập lệnh xây dựng (babel, webpack, jest).Không thể giả lập mô-đun có tính năng kích động và các cuộc gọi hàm thử nghiệm

Tôi đã viết một thành phần React mà tôi đang thử nghiệm. Thành phần này yêu cầu một tệp javascript khác, hiển thị một hàm.

search.js My nộp

export { 
    search, 
} 

function search(){ 
    // does things 
    return Promise.resolve('foo') 
} 

My phản ứng thành phần:

import React from 'react' 
import { search } from './search.js' 
import SearchResults from './SearchResults' 

export default SearchContainer { 
    constructor(){ 
    this.state = { 
     query: "hello world" 
    } 
    } 

    componentDidMount(){ 
    search(this.state.query) 
     .then(result => { this.setState({ result, })}) 
    } 

    render() { 
    return <SearchResults 
      result={this.state.result} 
      /> 
    } 
} 

Trong các thử nghiệm đơn vị của tôi, tôi muốn kiểm tra xem phương pháp search được gọi với các đối số chính xác.

xét nghiệm của tôi trông giống như thế:

import React from 'react'; 
import { shallow } from 'enzyme'; 
import should from 'should/as-function'; 

import SearchResults from './SearchResults'; 

let mockPromise; 

jest.mock('./search.js',() => { 
    return { search: jest.fn(() => mockPromise)}; 
}); 

import SearchContainer from './SearchContainer'; 

describe('<SearchContainer />',() => { 
    it('should call the search module',() => { 
    const result = { foo: 'bar' } 
    mockPromise = Promise.resolve(result); 
    const wrapper = shallow(<SearchContainer />); 

    wrapper.instance().componentDidMount(); 

    mockPromise.then(() => { 
     const searchResults = wrapper.find(SearchResults).first(); 
     should(searchResults.prop('result')).equal(result); 
    })  
    }) 
}); 

Tôi đã có một thời gian khó khăn để tìm ra cách để làm cho jest.mock làm việc, bởi vì nó đòi hỏi các biến phải bắt đầu bằng mock.

Nhưng nếu tôi muốn kiểm tra các đối số theo phương pháp search, tôi cần phải thực hiện chức năng mô phỏng trong các thử nghiệm của mình.

Nếu tôi thay đổi phần chế giễu, để sử dụng một biến:

const mockSearch = jest.fn(() => mockPromise) 
jest.mock('./search.js',() => { 
    return { search: mockSearch}; 
}); 

tôi nhận được lỗi này:

TypeError: (0 , _search.search) is not a function

Dù tôi cố gắng truy cập vào jest.fn và kiểm tra các đối số, tôi không thể làm cho nó hoạt động.

Tôi đang làm gì sai?

Trả lời

24

Sự cố

Lý do bạn nhận được lỗi đó liên quan đến cách hoạt động của nhiều thao tác khác nhau.

Mặc dù trong mã gốc của bạn, bạn chỉ nhập SearchContainersau gán một giá trị để mockSearch và gọi đùa của mock, các specs điểm ra rằng: Before instantiating a module, all of the modules it requested must be available.

Do đó, tại thời điểm SearchContainer được nhập, và lần lượt nhập search, biến số mockSearch của bạn vẫn chưa được xác định.

Người ta có thể thấy điều này lạ, vì nó cũng dường như ngụ ý search.js chưa được chế nhạo, và do đó chế nhạo sẽ không hoạt động chút nào. May mắn thay, (babel-) jest chắc chắn sẽ chuyển các cuộc gọi đến mock và các chức năng tương tự thậm chí cao hơn so với nhập khẩu, để chế độ nhạo báng sẽ hoạt động.

Tuy nhiên, việc gán mockSearch, được tham chiếu bởi chức năng của mô phỏng, sẽ không được đưa vào cuộc gọi mock.Vì vậy, thứ tự của các hoạt động có liên quan sẽ là một cái gì đó như:

  1. Đặt một nhà máy mock cho ./search.js
  2. nhập tất cả phụ thuộc, mà sẽ gọi cho nhà máy giả cho một chức năng để cung cấp cho các thành phần
  3. Gán một giá trị đến mockSearch

Khi bước 2 xảy ra, chức năng search được chuyển đến thành phần sẽ không được xác định và chuyển nhượng ở bước 3 quá muộn để thay đổi điều đó.

Giải pháp

Nếu bạn tạo các chức năng giả như một phần của mock cuộc gọi (như vậy mà nó sẽ được kéo lên quá), nó sẽ có một giá trị hợp lệ khi nó được nhập khẩu bởi module thành phần, càng sớm của bạn ví dụ cho thấy.

Như bạn đã chỉ ra, sự cố bắt đầu khi bạn muốn thực hiện chức năng mô phỏng trong các thử nghiệm của mình. Có một giải pháp rõ ràng cho việc này: nhập riêng mô-đun mà bạn đã mô phỏng.

Kể từ bây giờ bạn biết mocking jest thực sự xảy ra trước khi nhập khẩu, một cách tiếp cận tầm thường sẽ là:

import { search } from './search.js'; // This will actually be the mock 

jest.mock('./search.js',() => { 
    return { search: jest.fn(() => mockPromise) }; 
}); 

[...] 

beforeEach(() => { 
    search.mockClear(); 
}); 

it('should call the search module',() => { 
    [...] 

    expect(search.mock.calls.length).toBe(1); 
    expect(search.mock.calls[0]).toEqual(expectedArgs); 
}); 

Trong thực tế, bạn có thể muốn thay thế:

import { search } from './search.js'; 

Với:

const { search } = require.requireMock('./search.js'); 

Điều này sẽ không tạo ra bất kỳ sự khác biệt chức năng nào, nhưng có thể làm cho những gì bạn đang thực hiện hơn một chút nó (và sẽ giúp bất cứ ai sử dụng một hệ thống kiểm tra kiểu như Flow, vì vậy nó không nghĩ rằng bạn đang cố gắng gọi các chức năng giả trên search gốc).

Lưu ý bổ sung

Tất cả điều này chỉ thực sự cần thiết nếu những gì bạn cần giả là mặc định xuất một mô-đun. Nếu không (như @publicJorn chỉ ra), bạn có thể chỉ định lại thành viên có liên quan cụ thể trong các thử nghiệm, như vậy:

import * as search from './search.js'; 

beforeEach(() => { 
    search.search = jest.fn(() => mockPromise); 
}); 
+0

Cảm ơn phản hồi rất chi tiết này. Tôi chắc chắn sẽ thử giải pháp của bạn. – ghusse

+0

Trong khi giải pháp trước đây về mặt kỹ thuật nên hoạt động (và thậm chí tôi đã tìm ra lý do tại sao 'var' là cần thiết), bây giờ tôi đã chỉnh sửa câu trả lời để sử dụng một câu lệnh ít xấu xí hơn, IMO. – Tomty

+0

Cảm ơn sự giúp đỡ của bạn. Với giải pháp thứ hai, tôi gặp lỗi 'không thể đặt tìm kiếm thuộc tính của tìm kiếm'. Giải pháp đầu tiên hoạt động: Tôi cần phải nhập lib và gọi jest một lần cho tất cả (không phải trong trình kích hoạt beforeEach). – ghusse

Các vấn đề liên quan