6

Trong nhóm các nhà phát triển JavaScript của chúng tôi, chúng tôi đã chấp nhận phong cách redux/phản ứng của việc viết mã chức năng thuần túy. Tuy nhiên, dường như chúng tôi gặp sự cố khi kiểm tra mã của chúng tôi. Hãy xem xét ví dụ sau:Làm thế nào để kiểm tra một cây gọi hàm thuần túy trong sự cô lập?

function foo(data) { 
    return process({ 
     value: extractBar(data.prop1), 
     otherValue: extractBaz(data.prop2.someOtherProp) 
    }); 
} 

Chức năng này gọi phụ thuộc vào các cuộc gọi đến process, extractBarextractBaz, mỗi trong số đó có thể gọi các chức năng khác. Cùng nhau, họ có thể yêu cầu một mô hình không tầm thường để tham số data được xây dựng để thử nghiệm.

Nếu chúng ta chấp nhận sự cần thiết của việc tạo ra một đối tượng giả như vậy và thực sự làm như vậy trong các thử nghiệm, chúng tôi nhanh chóng tìm thấy chúng tôi có các trường hợp kiểm tra khó đọc và duy trì. Hơn nữa, nó rất có thể dẫn đến kiểm tra cùng một điều hơn và hơn, như các bài kiểm tra đơn vị cho process, extractBarextractBaz có lẽ cũng nên được viết. Thử nghiệm cho từng trường hợp cạnh có thể được thực hiện bởi các chức năng này thông qua giao diện foo khó sử dụng.


Chúng tôi có một vài giải pháp trong đầu, nhưng không thực sự thích bất kỳ giải pháp nào, vì dường như chúng tôi chưa từng thấy.

Giải pháp 1:

function foo(data, deps = defaultDeps) { 
    return deps.process({ 
     value: deps.extractBar(data.prop1), 
     otherValue: deps.extractBaz(data.prop2.someOtherProp) 
    }); 
} 

Giải pháp 2:

function foo(
    data, 
    processImpl = process, 
    extractBarImpl = extractBar, 
    extractBazImpl = extractBaz 
) { 
    return process({ 
     value: extractBar(data.prop1), 
     otherValue: extractBaz(data.prop2.someOtherProp) 
    }); 
} 

Giải pháp 2 ô nhiễm foo phương pháp chữ ký rất nhanh chóng khi số lượng chức năng phụ thuộc gọi tăng.

Giải pháp 3:

Chỉ cần chấp nhận sự thật rằng foo là một hoạt động hợp chất phức tạp và thử nghiệm nó như một toàn thể. Tất cả những hạn chế áp dụng.


Vui lòng đề xuất các khả năng khác. Tôi tưởng tượng đây là vấn đề mà cộng đồng lập trình chức năng phải giải quyết theo cách này hay cách khác.

Trả lời

6

Có thể bạn không cần bất kỳ giải pháp nào mà bạn đã cân nhắc. Một trong những khác biệt giữa lập trình chức năng và lập trình bắt buộc là kiểu chức năng nên tạo mã dễ dàng hơn để giải thích. Không chỉ theo nghĩa "chơi trình biên dịch" về tinh thần và mô phỏng những gì sẽ xảy ra với một tập hợp các đầu vào nhất định, mà còn lý luận về mã của bạn theo nghĩa nhiều hơn về toán học.

Ví dụ: mục tiêu kiểm tra đơn vị là kiểm tra "mọi thứ có thể bị hỏng". Nhìn vào đoạn mã đầu tiên bạn đăng, chúng tôi có thể giải thích về chức năng và hỏi, "Chức năng này có thể bị hỏng như thế nào?" Đó là một chức năng đủ đơn giản mà chúng ta không cần phải chơi trình biên dịch cả. Chúng ta chỉ có thể nói rằng hàm sẽ phá vỡ nếu hàm process() không trả về một giá trị chính xác cho một tập hợp các đầu vào đã cho, tức là nếu nó trả về một kết quả không hợp lệ hoặc nếu nó ném một ngoại lệ. Điều đó lần lượt ngụ ý rằng chúng ta cũng cần phải kiểm tra xem liệu extractBar()extractBaz() có trả về kết quả chính xác không, để chuyển các giá trị chính xác đến process().Vì vậy, thực sự, bạn chỉ cần kiểm tra xem foo() có ném ngoại lệ không mong muốn hay không, bởi vì tất cả những gì xảy ra là gọi số process() và bạn nên thử nghiệm process() trong tập hợp các bài kiểm tra đơn vị riêng của mình. Điều tương tự với extractBar()extractBaz(). Nếu hai hàm này trả về kết quả chính xác khi đưa vào các giá trị hợp lệ, chúng sẽ chuyển giá trị chính xác đến process() và nếu process() tạo kết quả chính xác khi được nhập các giá trị hợp lệ, thì foo() cũng sẽ trả lại kết quả chính xác.

Bạn có thể nói, "Còn đối số thì sao? Nếu nó trích xuất giá trị sai từ cấu trúc data thì sao?" Nhưng điều đó thực sự có thể phá vỡ? Nếu chúng ta nhìn vào hàm, nó sử dụng ký hiệu JS JS lõi để truy cập các thuộc tính trên một đối tượng. Chúng tôi không kiểm tra chức năng chính của ngôn ngữ trong bài kiểm tra đơn vị của chúng tôi cho ứng dụng của chúng tôi. Chúng ta chỉ có thể xem mã, lý do nó trích xuất các giá trị dựa trên truy cập thuộc tính mã hóa cứng và tiến hành với các thử nghiệm khác của chúng ta. Điều này không có nghĩa là bạn có thể vứt bỏ các bài kiểm tra đơn vị của mình, nhưng nhiều lập trình viên chức năng có kinh nghiệm thấy rằng họ cần ít bài kiểm tra hơn, vì bạn chỉ cần kiểm tra những thứ có thể phá vỡ và lập trình hàm giảm số lượng các thứ có thể bị vỡ để bạn có thể tập trung vào các thử nghiệm trên các bộ phận thực sự có nguy cơ. Và bằng cách này, nếu bạn đang làm việc với dữ liệu phức tạp, và bạn lo ngại rằng nó có thể khó khăn, ngay cả với FP, để giải thích tất cả các hoán vị có thể, bạn có thể muốn xem xét thử nghiệm sinh sản. Tôi nghĩ có một vài thư viện JS ở đó cho điều đó.

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