it-swarm-vi.com

Là tĩnh phổ biến "ác" cho thử nghiệm đơn vị và nếu vậy tại sao Resharper đề nghị nó?

Tôi đã thấy rằng chỉ có 3 cách để phụ thuộc kiểm tra đơn vị (mock/stub) là tĩnh trong C # .NET:

Cho rằng hai trong số này không miễn phí và một trong số đó chưa đạt được bản phát hành 1.0, việc nhạo báng các công cụ tĩnh không quá dễ dàng.

Điều đó có làm cho các phương thức tĩnh và "ác" như vậy (theo nghĩa thử nghiệm đơn vị) không? Và nếu vậy, tại sao resharper muốn tôi làm bất cứ điều gì có thể tĩnh, tĩnh? (Giả sử chia sẻ lại không phải là "xấu xa".)

Làm rõ: Tôi đang nói về kịch bản khi bạn muốn đơn vị kiểm tra một phương thức và phương thức đó gọi một phương thức tĩnh trong a khác nhau đơn vị/lớp. Theo hầu hết các định nghĩa về kiểm thử đơn vị, nếu bạn chỉ để phương thức được kiểm tra gọi phương thức tĩnh trong đơn vị/lớp khác thì bạn sẽ không kiểm tra đơn vị , bạn là tích hợp thử nghiệm. (Hữu ích, nhưng không phải là một bài kiểm tra đơn vị.)

87
Vaccano

Nhìn vào các câu trả lời khác ở đây, tôi nghĩ rằng có thể có một số nhầm lẫn giữa các phương thức tĩnh giữ trạng thái tĩnh hoặc gây ra tác dụng phụ (nghe có vẻ như là một ý tưởng thực sự tồi tệ) và các phương thức tĩnh chỉ trả về một giá trị.

Các phương pháp tĩnh không có trạng thái và không gây ra tác dụng phụ nên có thể dễ dàng kiểm tra đơn vị. Trên thực tế, tôi coi các phương pháp đó là một dạng lập trình chức năng "người nghèo"; bạn trao cho phương thức một đối tượng hoặc giá trị và nó trả về một đối tượng hoặc giá trị. Chỉ có bấy nhiêu thôi. Tôi không thấy các phương pháp như vậy sẽ ảnh hưởng tiêu cực đến thử nghiệm đơn vị như thế nào.

108
Robert Harvey

Bạn có vẻ khó hiểu tĩnh dữ liệ và tĩnh phương thức. Chia sẻ lại, nếu tôi nhớ chính xác, khuyên bạn nên tạo các phương thức private trong một lớp tĩnh nếu chúng có thể được thực hiện - tôi tin rằng điều này mang lại lợi ích hiệu suất nhỏ. Nó không khuyên bạn nên tạo "mọi thứ có thể" tĩnh!

Không có gì sai với các phương thức tĩnh và chúng rất dễ kiểm tra (miễn là chúng không thay đổi bất kỳ dữ liệu tĩnh nào). Ví dụ, hãy nghĩ về một thư viện Toán học, đây là ứng cử viên tốt cho một lớp tĩnh với các phương thức tĩnh. Nếu bạn có một phương thức (dự tính) như thế này:

public static long Square(int x)
{
    return x * x;
}

sau đó điều này là rõ ràng có thể kiểm tra và không có tác dụng phụ. Bạn chỉ cần kiểm tra xem khi bạn đi vào, giả sử, 20 bạn nhận lại 400. Không vấn đề gì.

27
Dan Diplo

Nếu câu hỏi thực sự ở đây là "Làm cách nào để kiểm tra mã này?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Sau đó, chỉ cần cấu trúc lại mã và thực hiện cuộc gọi đến lớp tĩnh như bình thường:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
18
Sunny

Statics không nhất thiết là xấu, nhưng chúng có thể giới hạn các lựa chọn của bạn khi nói đến thử nghiệm đơn vị với hàng giả/giả/cuống.

Có hai cách tiếp cận chung để chế giễu.

Cái đầu tiên (truyền thống - được thực hiện bởi RhinoMocks, Moq, NMock2; giả và cùi thủ công cũng ở trong trại này) cũng dựa vào các đường nối thử nghiệm và tiêm phụ thuộc. Giả sử bạn đang kiểm tra đơn vị một số mã tĩnh và nó có phụ thuộc. Điều thường xảy ra trong mã được thiết kế theo cách này là statics tạo ra các phụ thuộc của riêng chúng, đảo ngược đảo ngược phụ thuộc . Bạn sớm phát hiện ra rằng bạn không thể đưa các giao diện giả vào mã theo thử nghiệm được thiết kế theo cách này.

Cái thứ hai (giả định bất cứ thứ gì - được TypeMock, JustMock và Moles thực hiện) dựa trên .NET's API Profiling . Nó có thể chặn bất kỳ hướng dẫn CIL nào của bạn và thay thế một đoạn mã của bạn bằng mã giả. Điều này cho phép TypeMock và các sản phẩm khác trong trại này chế giễu bất cứ điều gì: thống kê, lớp niêm phong, phương pháp riêng tư - những thứ không được thiết kế để có thể kiểm tra được.

Có một cuộc tranh luận đang diễn ra giữa hai trường phái tư tưởng. Một người nói, hãy làm theo nguyên tắc RẮN và thiết kế để kiểm tra (thường bao gồm việc dễ dàng trong việc thống kê). Một người khác nói, hãy mua TypeMock và đừng lo lắng.

15
azheglov

Hãy xem điều này: "Phương thức tĩnh là cái chết đối với khả năng kiểm tra" . Tóm tắt ngắn về đối số:

Để kiểm tra đơn vị, bạn cần lấy một đoạn mã nhỏ, viết lại các phụ thuộc của nó và kiểm tra nó một cách riêng lẻ. Điều này thật khó với các phương thức tĩnh, không chỉ trong trường hợp họ truy cập trạng thái toàn cầu mà ngay cả khi họ chỉ gọi các phương thức tĩnh khác.

14
Rafał Dowgird

Một sự thật đơn giản hiếm khi được thừa nhận là nếu một lớp chứa một phụ thuộc có thể nhìn thấy của trình biên dịch trên một lớp khác, thì nó không thể được kiểm tra tách biệt khỏi lớp đó. Bạn có thể giả mạo một cái gì đó trông giống như một bài kiểm tra và sẽ xuất hiện trên một báo cáo như thể đó là một bài kiểm tra.

Nhưng nó sẽ không có các thuộc tính xác định chính của một bài kiểm tra; thất bại khi mọi thứ sai, vượt qua khi họ đúng.

Điều này áp dụng cho mọi cuộc gọi tĩnh, lệnh gọi hàm tạo và mọi tham chiếu đến các phương thức hoặc trường không được kế thừa từ lớp cơ sở hoặc giao diện . Nếu tên lớp xuất hiện trong mã, thì đó là một phụ thuộc có thể nhìn thấy của trình biên dịch và bạn không thể kiểm tra hợp lệ nếu không có nó. Bất kỳ khối nhỏ hơn chỉ đơn giản là không phải là đơn vị kiểm tra hợp lệ. Bất kỳ nỗ lực nào để coi nó như thể nó sẽ có kết quả không có ý nghĩa hơn việc viết một tiện ích nhỏ để phát ra XML được sử dụng bởi khung kiểm tra của bạn để nói 'thử nghiệm đã qua'.

Cho rằng, có ba lựa chọn:

  1. định nghĩa kiểm thử đơn vị là kiểm thử đơn vị gồm một lớp và nó phụ thuộc mã hóa cứng. Điều này hoạt động, cung cấp cho bạn tránh phụ thuộc tròn.

  2. không bao giờ tạo phụ thuộc thời gian biên dịch giữa các lớp bạn chịu trách nhiệm kiểm tra. Điều này hoạt động, cung cấp cho bạn không quan tâm đến kiểu mã kết quả.

  3. không kiểm tra đơn vị, kiểm tra tích hợp thay thế. Cái nào hoạt động, cung cấp nó không xung đột với cái gì khác mà bạn cần để sử dụng thuật ngữ kiểm tra tích hợp.

5
soru

Không có hai cách về nó. Các đề xuất của ReSharper và một số tính năng hữu ích của C # sẽ không được sử dụng thường xuyên nếu bạn đang viết các bài kiểm tra đơn vị nguyên tử bị cô lập cho tất cả mã của mình.

Ví dụ, nếu bạn có một phương thức tĩnh và bạn cần loại bỏ nó, bạn không thể trừ khi bạn sử dụng khung cách ly dựa trên hồ sơ. Cách giải quyết tương thích với cuộc gọi là thay đổi đỉnh của phương thức để sử dụng ký hiệu lambda. Ví dụ:

TRƯỚC:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

SAU:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

Hai là tương thích cuộc gọi. Người gọi không phải thay đổi. Cơ thể của chức năng vẫn giữ nguyên.

Sau đó, trong mã Kiểm tra đơn vị của bạn, bạn có thể thực hiện cuộc gọi này như vậy (giả sử đó là trong một lớp có tên là Cơ sở dữ liệu):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Hãy cẩn thận để thay thế nó bằng giá trị ban đầu sau khi bạn hoàn thành. Bạn có thể thực hiện điều đó thông qua một lần thử/cuối cùng hoặc, trong phần kiểm tra đơn vị của bạn, phần được gọi sau mỗi bài kiểm tra, viết mã như sau:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

cái này sẽ gọi lại bộ khởi tạo tĩnh của lớp bạn.

Lambda Funcs không hỗ trợ nhiều như các phương thức tĩnh thông thường, vì vậy phương pháp này có các tác dụng phụ không mong muốn sau:

  1. Nếu phương thức tĩnh là phương thức mở rộng, trước tiên bạn phải thay đổi nó thành phương thức không mở rộng. Resharper có thể làm điều này cho bạn tự động.
  2. Nếu bất kỳ kiểu dữ liệu nào của các phương thức tĩnh là một hội đồng liên kết nhúng, chẳng hạn như đối với Office, bạn phải bọc phương thức, bọc kiểu hoặc thay đổi thành kiểu 'đối tượng'.
  3. Bạn không còn có thể sử dụng công cụ tái cấu trúc chữ ký thay đổi của Resharper.

Nhưng hãy nói rằng bạn tránh hoàn toàn thống kê và bạn chuyển đổi nó thành một phương thức cá thể. Nó vẫn không thể chế nhạo trừ khi phương thức này là ảo hoặc được triển khai như một phần của giao diện.

Vì vậy, trong thực tế, bất cứ ai đề xuất biện pháp khắc phục các phương thức tĩnh là biến chúng thành các phương thức cá thể, chúng cũng sẽ chống lại các phương thức cá thể không phải là ảo hoặc là một phần của giao diện.

Vậy tại sao C # có phương thức tĩnh? Tại sao nó cho phép các phương thức cá thể không ảo?

Nếu bạn sử dụng một trong hai "Tính năng" này, thì đơn giản là bạn không thể tạo các phương thức bị cô lập.

Vậy khi nào bạn sử dụng chúng?

Sử dụng chúng cho bất kỳ mã nào mà bạn không mong đợi bất kỳ ai muốn khai thác. Một số ví dụ: phương thức Format () của lớp String, phương thức WriteLine () của lớp Console, phương thức Cosh () của lớp Math

Và một điều nữa .. Hầu hết mọi người sẽ không quan tâm đến điều này, nhưng nếu bạn có thể về hiệu suất của một cuộc gọi gián tiếp, đó là một lý do khác để tránh các phương thức cá thể. Có những trường hợp khi đó là một hiệu suất hit. Đó là lý do tại sao các phương thức không ảo tồn tại ở nơi đầu tiên.

4
zumalifeguard

Tôi thấy rằng sau một thời gian dài, không ai nói rõ một sự thật đơn giản. Nếu resharper nói với tôi rằng tôi có thể làm cho một phương thức tĩnh, nó có nghĩa là một điều rất lớn đối với tôi, tôi có thể nghe giọng nói của anh ấy nói với tôi: "này, bạn, những mảnh logic này không phải là TRÁCH NHIỆM của lớp hiện tại để xử lý, vì vậy nó nên tránh xa trong một số lớp người trợ giúp hoặc một cái gì đó ".

3
g1ga
  1. Tôi tin rằng đó là một phần vì các phương thức tĩnh "gọi" nhanh hơn các phương thức cá thể. (Trong dấu ngoặc kép vì mùi này tối ưu hóa vi mô) xem http://dotnetperls.com/static-method
  2. Nó nói với bạn rằng nó không cần trạng thái, do đó có thể được gọi từ bất cứ đâu, loại bỏ chi phí xúi giục nếu đó là điều duy nhất ai đó cần.
  3. Nếu tôi muốn chế giễu nó, thì tôi nghĩ đó thường là cách thực hành mà nó đã tuyên bố trên một giao diện.
  4. Nếu nó được khai báo trên một giao diện thì R # sẽ không đề nghị bạn làm cho nó tĩnh.
  5. Nếu nó được khai báo là ảo, thì R # sẽ không đề nghị bạn làm cho nó tĩnh.
  6. Giữ trạng thái (trường) tĩnh là điều cần luôn được xem xét một cách cẩn thận . Trạng thái tĩnh và chủ đề trộn như lithium và nước.

R # không phải là công cụ duy nhất sẽ đưa ra gợi ý này. Phân tích mã FxCop/MS cũng sẽ làm tương tự.

Tôi thường nói rằng nếu phương thức này là tĩnh, nói chung nó cũng có thể kiểm tra được. Điều đó mang lại một số cân nhắc về thiết kế và có thể thảo luận nhiều hơn những gì tôi có trong ngón tay của mình ngay bây giờ, vì vậy hãy kiên nhẫn chờ đợi những phiếu bầu và bình luận ...;)

3
MIA

Nếu phương thức tĩnh được gọi từ bên trong phương thức khác, không thể ngăn chặn hoặc thay thế cuộc gọi như vậy. Điều này có nghĩa, hai phương thức này tạo thành một Đơn vị duy nhất; Kiểm tra đơn vị của bất kỳ loại kiểm tra cả hai.

Và nếu phương thức tĩnh này nói chuyện với Internet, kết nối cơ sở dữ liệu, hiển thị cửa sổ bật lên GUI hoặc chuyển đổi kiểm tra Đơn vị thành mớ hỗn độn, thì nó chỉ thực hiện mà không có bất kỳ công việc dễ dàng nào xung quanh. Một phương thức gọi phương thức tĩnh như vậy không thể kiểm tra được nếu không tái cấu trúc, ngay cả khi nó có rất nhiều mã tính toán thuần túy sẽ có lợi rất cao từ việc được kiểm tra đơn vị.

2
h22

Tôi tin rằng Resharper đang cung cấp cho bạn thông tin và áp dụng các nguyên tắc mã hóa mà nó đã được thiết lập. Khi tôi đã sử dụng Resharper và nó đã nói với tôi rằng một phương thức nên tĩnh, nó bị ràng buộc là một phương thức riêng không hoạt động trên bất kỳ biến đối tượng nào.

Bây giờ, đối với khả năng kiểm tra, kịch bản này không phải là một vấn đề vì dù sao bạn cũng không nên thử nghiệm các phương thức riêng tư.

Đối với khả năng kiểm tra của các phương thức tĩnh là công khai thì kiểm thử đơn vị sẽ trở nên khó khăn khi các phương thức tĩnh chạm vào trạng thái tĩnh. Cá nhân tôi sẽ giữ điều này ở mức tối thiểu và sử dụng các phương thức tĩnh càng nhiều hàm càng tốt khi có bất kỳ sự phụ thuộc nào được truyền vào phương thức có thể được điều khiển thông qua một vật cố thử nghiệm. Tuy nhiên đây là một quyết định thiết kế.

0
aqwert