Kiểm thử đơn vị phương thức riêng trong c ++ bằng một lớp bạn


15

Tôi biết rằng đây là một thực tiễn tranh luận, nhưng hãy giả sử rằng đây là lựa chọn tốt nhất cho tôi. Tôi đang tự hỏi về kỹ thuật thực tế để làm điều này là gì. Cách tiếp cận mà tôi thấy là thế này:

1) Tạo một lớp bạn mà lớp tôi muốn kiểm tra.

2) Trong lớp bạn bè, tạo (các) phương thức công khai gọi (các) phương thức riêng của lớp được kiểm tra.

3) Kiểm tra các phương thức công khai của lớp bạn bè.

Dưới đây là một ví dụ đơn giản để minh họa các bước trên:

#include <iostream>

class MyClass
{
  friend class MyFriend; // Step 1

  private:
  int plus_two(int a)
  {
    return a + 2;
  }
};

class MyFriend
{
public:
  MyFriend(MyClass *mc_ptr_1)
  {
    MyClass *mc_ptr = mc_ptr_1;
  }

  int plus_two(int a) // Step 2
  {
    return mc_ptr->plus_two(a);
  }
private:
  MyClass *mc_ptr;
};

int main()
{
  MyClass mc;
  MyFriend mf(&mc);
  if (mf.plus_two(3) == 5) // Step 3
    {
      std::cout << "Passed" << std::endl;
    }
  else
    {
      std::cout << "Failed " << std::endl;
    }

  return 0;
}

Biên tập:

Tôi thấy rằng trong các cuộc thảo luận sau một trong những câu trả lời mọi người đang tự hỏi về cơ sở mã của tôi.

Lớp của tôi có các phương thức được gọi bằng các phương thức khác; không có phương thức nào trong số các phương thức này được gọi bên ngoài lớp, vì vậy chúng nên ở chế độ riêng tư. Tất nhiên chúng có thể được đưa vào một phương thức, nhưng về mặt logic thì chúng tách biệt tốt hơn nhiều. Các phương pháp này đủ phức tạp để đảm bảo kiểm tra đơn vị, và do vấn đề về hiệu suất, tôi rất có thể sẽ phải tính lại các phương thức này, do đó, sẽ rất tốt nếu có một thử nghiệm để đảm bảo rằng việc bao thanh toán lại của tôi không phá vỡ bất cứ điều gì. Tôi không phải là người duy nhất làm việc trong nhóm, mặc dù tôi là người duy nhất đang làm việc trong dự án này bao gồm các bài kiểm tra.

Như đã nói ở trên, câu hỏi của tôi không phải là liệu có nên viết bài kiểm tra đơn vị cho các phương pháp riêng tư hay không, mặc dù tôi đánh giá cao phản hồi.


5
Đây là một gợi ý đáng ngờ cho một câu hỏi nghi vấn. Tôi không thích sự kết hợp của bạn bè khi mã được phát hành phải biết về bài kiểm tra. Câu trả lời của Nir dưới đây là một cách để giảm bớt điều đó, nhưng tôi vẫn không thích thay đổi lớp học để phù hợp với bài kiểm tra. Vì tôi không dựa vào sự kế thừa thường xuyên, đôi khi tôi chỉ làm cho các phương thức riêng tư được bảo vệ và có một lớp kiểm tra kế thừa và phơi bày khi cần thiết. Tôi mong đợi ít nhất ba "boo hisses" cho nhận xét này, nhưng thực tế là API công khai và API thử nghiệm có thể khác nhau và vẫn khác biệt với API riêng. Meh.
J Trana

4
@JTrana: Tại sao không viết nó lên như một câu trả lời thích hợp?
Bart van Ingen Schenau

Đồng ý. Đây không phải là một trong những người bạn cảm thấy siêu tự hào về nó, nhưng hy vọng nó sẽ giúp ích.
J Trana

Câu trả lời:


23

Một thay thế cho bạn bè (tốt, theo một nghĩa nào đó) mà tôi sử dụng thường xuyên là một mẫu mà tôi đã biết là access_by. Nó khá đơn giản:

class A {
  void priv_method(){};
 public:
  template <class T> struct access_by;
  template <class T> friend struct access_by;
}

Bây giờ, giả sử lớp B có liên quan đến thử nghiệm A. Bạn có thể viết điều này:

template <> struct access_by<B> {
  call_priv_method(A & a) {a.priv_method();}
}

Sau đó, bạn có thể sử dụng chuyên môn này của access_by để gọi các phương thức riêng tư của A. Về cơ bản, điều này thực sự đặt trách nhiệm tuyên bố tình bạn vào tệp tiêu đề của lớp muốn gọi các phương thức riêng tư của A. Nó cũng cho phép bạn thêm bạn bè vào A mà không thay đổi nguồn của A. Về mặt thành ngữ, nó cũng chỉ ra cho bất cứ ai đọc nguồn của A mà A không chỉ ra B là một người bạn thực sự theo nghĩa mở rộng giao diện của nó. Thay vào đó, giao diện của A hoàn chỉnh như đã cho và B cần quyền truy cập đặc biệt vào A (thử nghiệm là một ví dụ tốt, tôi cũng đã sử dụng mẫu này khi triển khai các ràng buộc python, đôi khi một chức năng cần riêng tư trong C ++ rất tiện để tiếp xúc với lớp python để thực hiện).


Tò mò về một trường hợp sử dụng hợp lệ để tạo ra friend access_by, không phải là người bạn đầu tiên đủ - là một cấu trúc lồng nhau, nó sẽ có quyền truy cập vào mọi thứ trong A? ví dụ. coliru.stacked-crooked.com/a/663dd17ed2acd7a3
hương thơm

10

Nếu nó khó kiểm tra, nó viết rất tệ

Nếu bạn có một lớp với các phương thức riêng đủ phức tạp để đảm bảo kiểm tra riêng của chúng, thì lớp đó đang làm quá nhiều. Bên trong có một lớp khác đang cố gắng thoát ra.

Trích xuất các phương thức riêng tư mà bạn muốn kiểm tra vào một lớp mới (hoặc các lớp) và đặt chúng ở chế độ công khai. Kiểm tra các lớp mới.

Ngoài việc làm cho mã dễ kiểm tra hơn, việc tái cấu trúc này sẽ giúp mã dễ hiểu và duy trì hơn.


1
Tôi hoàn toàn đồng ý với câu trả lời này, nếu bạn không thể kiểm tra hoàn toàn các phương thức riêng tư của mình bằng cách kiểm tra các phương thức công khai, thì có gì đó không đúng và tái cấu trúc các phương thức riêng cho lớp của chúng sẽ là một giải pháp tốt.
David Perfors

4
Trong cơ sở mã của tôi, một lớp có một phương thức rất phức tạp để khởi tạo một biểu đồ tính toán. Nó gọi một số chức năng phụ theo trình tự để đạt được các khía cạnh khác nhau của điều này. Mỗi hàm phụ khá phức tạp và tổng cộng của mã rất phức tạp. Tuy nhiên, các hàm phụ là vô nghĩa nếu không được gọi trên lớp này và theo đúng trình tự. Tất cả những gì người dùng quan tâm là biểu đồ tính toán được khởi tạo hoàn toàn; các trung gian là vô giá trị cho người dùng. Xin vui lòng, tôi muốn nghe làm thế nào tôi nên cấu trúc lại điều này, và tại sao nó có ý nghĩa hơn là chỉ thử nghiệm các phương thức riêng tư.
Nir Friedman

1
@Nir: Thực hiện một cách tầm thường: trích xuất một lớp với tất cả các phương thức đó công khai và làm cho lớp hiện tại của bạn trở thành mặt tiền xung quanh lớp mới.
kevin cline

Câu trả lời này là đúng nhưng nó thực sự phụ thuộc vào dữ liệu bạn đang làm việc. Trong trường hợp của tôi, tôi không được cung cấp dữ liệu thử nghiệm thực tế nên tôi phải tạo một số bằng cách quan sát dữ liệu thời gian thực và sau đó "tiêm" vào ứng dụng của mình và xem nó đi. Một phần dữ liệu quá phức tạp để xử lý nên việc tạo dữ liệu thử nghiệm một phần sẽ dễ dàng hơn nhiều, chỉ là một tập hợp con của dữ liệu thời gian thực để nhắm mục tiêu từng dữ liệu so với thực tế tái tạo dữ liệu thời gian thực. Chức năng riêng tư không phức tạp đủ để kêu gọi thực hiện một số lớp nhỏ khác (với chức năng tương ứng)
rbaleksandar

4

Bạn không nên thử nghiệm các phương pháp riêng tư. Giai đoạn = Stage. Các lớp sử dụng lớp của bạn chỉ quan tâm đến các phương thức mà nó cung cấp, chứ không phải các phương thức mà nó sử dụng dưới mui xe để làm việc.

Nếu bạn lo lắng về phạm vi bảo hiểm mã của mình, bạn cần tìm các cấu hình cho phép bạn kiểm tra phương thức riêng tư đó từ một trong các cuộc gọi phương thức công khai. Nếu bạn không thể làm điều đó, thì điểm đầu tiên của phương pháp là gì? Nó chỉ đơn giản là mã không thể truy cập.


6
Quan điểm của các phương pháp riêng là giảm bớt sự phát triển (thông qua các mối quan tâm, hoặc duy trì DRY, hoặc bất kỳ số lượng nào) nhưng có nghĩa là không vĩnh viễn. Họ là riêng tư vì lý do đó. Chúng có thể xuất hiện, biến mất hoặc thay đổi chức năng mạnh mẽ từ lần thực hiện này sang lần thực hiện tiếp theo, đó là lý do tại sao việc buộc chúng vào các bài kiểm tra đơn vị không phải lúc nào cũng thực tế hoặc thậm chí hữu ích.
Ampt

8
Nó có thể không phải lúc nào cũng thực tế hoặc hữu ích, nhưng đó là một điều rất xa khi nói rằng bạn không bao giờ nên thử nghiệm chúng. Bạn đang nói về các phương thức riêng tư như thể chúng là phương thức riêng tư của người khác; "chúng có thể xuất hiện, biến mất ...". Không, họ không thể. Nếu bạn là đơn vị kiểm tra chúng trực tiếp, thì đó chỉ là do bạn tự bảo trì chúng. Nếu bạn thay đổi việc thực hiện, bạn thay đổi các bài kiểm tra. Nói tóm lại, tuyên bố chăn của bạn là không chính đáng. Mặc dù thật tốt khi cảnh báo OP về vấn đề này, câu hỏi của anh ấy vẫn hợp lý và câu trả lời của bạn không thực sự trả lời nó.
Nir Friedman

2
Tôi cũng xin lưu ý: OP đã nói trước rằng anh ấy biết rằng đó là một thực tiễn tranh luận. Vì vậy, nếu anh ta muốn làm điều đó, có lẽ anh ta thực sự có lý do tốt cho nó? Không ai trong chúng tôi biết chi tiết về cơ sở mã của anh ấy. Trong mã tôi làm việc cùng, chúng tôi có một số lập trình viên rất có kinh nghiệm và chuyên gia và họ nghĩ rằng nó rất hữu ích để kiểm thử các phương thức riêng tư trong một số trường hợp.
Nir Friedman

2
-1, một câu trả lời như "Bạn không nên thử nghiệm các phương pháp riêng tư." là IMHO không hữu ích. Chủ đề khi nào nên kiểm tra và khi nào không kiểm tra các phương pháp riêng tư đã được thảo luận đầy đủ trên trang web này. OP đã chỉ ra rằng anh ta nhận thức được cuộc thảo luận này, và rõ ràng anh ta đang tìm kiếm giải pháp theo giả định rằng trong trường hợp của anh ta thử nghiệm các phương pháp riêng tư là cách để đi.
Doc Brown

3
Tôi nghĩ vấn đề ở đây là đây có thể là một vấn đề XY rất kinh điển ở chỗ OP nghĩ rằng anh ta cần phải thử nghiệm các phương pháp riêng tư của mình vì lý do này hay lý do khác, khi thực tế anh ta có thể tiếp cận thử nghiệm từ quan điểm thực dụng hơn, xem riêng tư các phương thức như các hàm trợ giúp đơn thuần cho các công chúng, là hợp đồng với người dùng cuối của lớp.
Ampt

3

Có một vài lựa chọn để làm điều này, nhưng hãy nhớ rằng chúng (về bản chất) sửa đổi giao diện chung của các mô-đun của bạn, để cung cấp cho bạn quyền truy cập vào chi tiết triển khai nội bộ (chuyển đổi thử nghiệm đơn vị thành các phụ thuộc máy khách được liên kết chặt chẽ, nơi bạn nên có không phụ thuộc chút nào).

  • bạn có thể thêm khai báo một người bạn (lớp hoặc hàm) vào lớp được kiểm tra.

  • bạn có thể thêm #define private publicvào phần đầu của các tệp thử nghiệm của mình trước khi #includesử dụng mã được thử nghiệm. Trong trường hợp mã được kiểm tra là một thư viện đã được biên dịch, điều này có thể làm cho các tiêu đề không còn khớp với mã nhị phân đã được biên dịch (và gây ra UB).

  • bạn có thể chèn một macro trong lớp được kiểm tra của bạn và quyết định vào ngày sau đó macro đó có nghĩa là gì (với một định nghĩa khác cho mã kiểm tra). Điều này sẽ cho phép bạn kiểm tra nội bộ, nhưng nó cũng sẽ cho phép mã máy khách của bên thứ ba xâm nhập vào lớp của bạn (bằng cách tạo định nghĩa riêng của chúng trong khai báo bạn thêm).


2

Đây là một gợi ý đáng ngờ cho một câu hỏi nghi vấn. Tôi không thích sự kết hợp của bạn bè khi mã được phát hành phải biết về bài kiểm tra. Câu trả lời của Nir là một cách để giảm bớt điều đó, nhưng tôi vẫn không thích thay đổi lớp học để phù hợp với bài kiểm tra.

Vì tôi không dựa vào sự kế thừa thường xuyên, đôi khi tôi chỉ làm cho các phương thức riêng tư được bảo vệ và có một lớp kiểm tra kế thừa và phơi bày khi cần thiết. Thực tế là API công khai và API thử nghiệm có thể khác nhau và vẫn khác biệt với API riêng, khiến bạn bị ràng buộc.

Đây là một ví dụ thực tế đó là loại tôi dùng đến thủ thuật này. Tôi viết mã nhúng và chúng tôi dựa vào các máy trạng thái khá nhiều. API bên ngoài không nhất thiết phải biết về trạng thái máy bên trong, nhưng thử nghiệm (có thể được cho là) ​​kiểm tra sự phù hợp với sơ đồ máy trạng thái trong tài liệu thiết kế. Tôi có thể hiển thị một getter "trạng thái hiện tại" như được bảo vệ và sau đó cấp quyền truy cập kiểm tra vào đó, cho phép tôi kiểm tra máy trạng thái đầy đủ hơn. Tôi thường thấy loại lớp này khó kiểm tra như một hộp đen.


Mặc dù là một cách tiếp cận Java, nhưng điều này khá chuẩn để làm cho các hàm riêng ở mức mặc định thay vì cho phép các lớp khác trong cùng một gói (các lớp kiểm tra) có thể nhìn thấy chúng.

0

Bạn có thể viết mã của mình với nhiều cách giải quyết để ngăn bạn phải sử dụng bạn bè.

Bạn có thể viết các lớp và không bao giờ có bất kỳ phương thức riêng tư nào cả. Tất cả những gì bạn cần làm là tạo các hàm triển khai trong đơn vị biên dịch, hãy để lớp của bạn gọi chúng và truyền vào bất kỳ thành viên dữ liệu nào chúng cần truy cập.

Và vâng, điều đó có nghĩa là bạn có thể thay đổi chữ ký hoặc thêm các phương thức "triển khai" mới mà không thay đổi tiêu đề của bạn trong tương lai.

Bạn phải cân nhắc dù có đáng hay không. Và rất nhiều thứ sẽ thực sự phụ thuộc vào người sẽ xem tiêu đề của bạn.

Nếu tôi đang sử dụng thư viện của bên thứ 3, tôi không muốn thấy khai báo bạn bè cho người kiểm tra đơn vị của họ. Tôi cũng không muốn xây dựng thư viện của họ và chạy thử nghiệm khi tôi làm. Thật không may, quá nhiều thư viện mã nguồn mở của bên thứ 3 tôi đã xây dựng làm điều đó.

Kiểm tra là công việc của các nhà văn của thư viện, không phải người dùng của nó.

Tuy nhiên, không phải tất cả các lớp đều hiển thị cho người dùng thư viện của bạn. Rất nhiều lớp đang "triển khai" và bạn triển khai chúng theo cách tốt nhất để đảm bảo chúng hoạt động đúng. Trong đó, bạn vẫn có thể có các phương thức và thành viên riêng nhưng muốn người kiểm thử đơn vị kiểm tra chúng. Vì vậy, hãy tiếp tục và làm theo cách đó nếu điều đó sẽ dẫn đến mã mạnh hơn nhanh hơn, dễ duy trì cho những người cần làm như vậy.

Nếu tất cả người dùng trong lớp của bạn đều ở trong công ty hoặc nhóm của riêng bạn, bạn cũng có thể thư giãn thêm một chút về chiến lược đó, giả sử nó được cho phép theo tiêu chuẩn mã hóa của công ty bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.