Khó khăn với TDD & tái cấu trúc (Hoặc - Tại sao điều này lại đau đớn hơn thế?)


20

Tôi muốn dạy bản thân sử dụng phương pháp TDD và tôi đã có một dự án mà tôi đã muốn thực hiện trong một thời gian. Đó không phải là một dự án lớn nên tôi nghĩ nó sẽ là một ứng cử viên sáng giá cho TDD. Tuy nhiên, tôi cảm thấy như có gì đó không ổn. Để tôi lấy một ví dụ:

Ở cấp độ cao, dự án của tôi là một bổ trợ cho Microsoft OneNote cho phép tôi theo dõi và quản lý Dự án dễ dàng hơn. Bây giờ, tôi cũng muốn giữ logic kinh doanh cho việc này được tách rời khỏi OneNote trong trường hợp có thể trong trường hợp tôi quyết định xây dựng bộ lưu trữ tùy chỉnh của riêng mình và kết thúc một ngày nào đó.

Đầu tiên tôi bắt đầu với một bài kiểm tra chấp nhận từ đơn giản cơ bản để phác thảo những gì tôi muốn tính năng đầu tiên của mình làm. Nó trông giống như thế này (làm giảm nhiệt độ của nó):

  1. Nhấp chuột tạo dự án
  2. Kiểu người dùng trong tiêu đề của dự án
  3. Xác minh rằng dự án được tạo chính xác

Bỏ qua các công cụ UI và một số kế hoạch trung gian Tôi đến thử nghiệm đơn vị đầu tiên của mình:

[TestMethod]
public void CreateProject_BasicParameters_ProjectIsValid()
{
    var testController = new Controller();
    Project newProject = testController(A.Dummy<String>());
    Assert.IsNotNull(newProject);
}

Càng xa càng tốt. Đỏ, xanh lá cây, tái cấu trúc, v.v ... Bây giờ nó cần thực sự tiết kiệm công cụ. Cắt ra một số bước ở đây tôi kết thúc với điều này.

[TestMethod]
public void CreateProject_BasicParameters_ProjectMatchesExpected()
{
    var fakeDataStore = A.Fake<IDataStore>();
    var testController = new Controller(fakeDataStore);
    String expectedTitle = fixture.Create<String>("Title");
    Project newProject = testController(expectedTitle);

    Assert.AreEqual(expectedTitle, newProject.Title);
}

Tôi vẫn cảm thấy tốt ở điểm này. Tôi chưa có một kho lưu trữ dữ liệu cụ thể, nhưng tôi đã tạo giao diện như tôi dự đoán nó sẽ trông như thế nào.

Tôi sẽ bỏ qua một vài bước ở đây vì bài đăng này đã đủ dài, nhưng tôi đã làm theo các quy trình tương tự và cuối cùng tôi nhận được bài kiểm tra này cho kho dữ liệu của mình:

[TestMethod]
public void SaveNewProject_BasicParameters_RequestsNewPage()
{
    /* snip init code */
    testDataStore.SaveNewProject(A.Dummy<IProject>());
    A.CallTo(() => oneNoteInterop.SavePage()).MustHaveHappened();
}

Điều này là tốt cho đến khi tôi cố gắng thực hiện nó:

public String SaveNewProject(IProject project)
{
    Page projectPage = oneNoteInterop.CreatePage(...);
}

Và đó là vấn đề ngay tại nơi "...". Bây giờ tôi nhận ra rằng tại điểm NÀY, CreatPage yêu cầu ID phần. Tôi đã không nhận ra điều này khi tôi nghĩ ở cấp độ bộ điều khiển vì tôi chỉ quan tâm đến việc kiểm tra các bit có liên quan đến bộ điều khiển. Tuy nhiên, đến tận bây giờ tôi mới nhận ra mình phải hỏi người dùng địa điểm để lưu trữ dự án. Bây giờ tôi phải thêm ID vị trí vào kho dữ liệu, sau đó thêm một vào dự án, sau đó thêm một vào bộ điều khiển và thêm nó vào TẤT CẢ các bài kiểm tra đã được viết cho tất cả những điều đó. Nó đã trở nên tẻ nhạt rất nhanh và tôi không thể không cảm thấy mình sẽ nắm bắt được điều này nhanh hơn nếu tôi phác thảo thiết kế trước thời hạn thay vì để nó được thiết kế trong quá trình TDD.

Ai đó có thể vui lòng giải thích cho tôi nếu tôi đã làm điều gì sai trong quá trình này? Có cách nào để tái cấu trúc loại này có thể tránh được không? Hay điều này là phổ biến? Nếu nó là phổ biến thì có cách nào làm cho nó không đau hơn không?

Cảm ơn tất cả!


Bạn sẽ nhận được một số nhận xét sâu sắc nếu bạn đăng chủ đề này tại diễn đàn thảo luận này: Groups.google.com.vn/forum/#!forum/ , đặc biệt dành cho các chủ đề TDD.
Chuck Krutsinger

1
Nếu bạn cần thêm một cái gì đó vào tất cả các bài kiểm tra của mình thì có vẻ như bài kiểm tra của bạn được viết kém. Bạn nên cấu trúc lại các bài kiểm tra của mình và xem xét sử dụng một vật cố hợp lý.
Dave Hillier

Câu trả lời:


19

Mặc dù TDD (đúng) được quảng cáo là một cách để thiết kế và phát triển phần mềm của bạn, nhưng vẫn nên suy nghĩ về thiết kế và kiến ​​trúc trước đó. IMO, "phác thảo thiết kế trước thời hạn" là trò chơi công bằng. Thường thì điều này sẽ ở mức cao hơn so với các quyết định thiết kế mà bạn sẽ được đưa đến thông qua TDD, tuy nhiên.

Cũng đúng là khi mọi thứ thay đổi, bạn thường sẽ phải cập nhật các bài kiểm tra. Không có cách nào để loại bỏ hoàn toàn điều này, nhưng có một số điều bạn có thể làm để làm cho các xét nghiệm của mình bớt giòn và giảm thiểu cơn đau.

  1. Càng nhiều càng tốt, giữ cho chi tiết thực hiện trong các bài kiểm tra của bạn. Điều này có nghĩa là chỉ kiểm tra thông qua các phương thức công khai và khi có thể ưu tiên xác minh dựa trên tương tác . Nói cách khác, nếu bạn kiểm tra kết quả của một thứ gì đó chứ không phải là các bước để đạt được điều đó, các bài kiểm tra của bạn sẽ ít dễ vỡ hơn.

  2. Giảm thiểu sự trùng lặp trong mã kiểm tra của bạn, giống như bạn làm trong mã sản xuất. Bài này là một tài liệu tham khảo tốt. Trong ví dụ của bạn, có vẻ như thật đau đớn khi thêm thuộc IDtính vào hàm tạo của bạn vì bạn đã gọi hàm tạo trực tiếp trong một số thử nghiệm khác nhau. Thay vào đó, hãy thử trích xuất việc tạo đối tượng cho một phương thức hoặc khởi tạo nó một lần cho mỗi thử nghiệm trong một phương thức khởi tạo thử nghiệm.


Tôi đã đọc những ưu điểm của dựa trên trạng thái so với dựa trên tương tác và hiểu nó hầu hết thời gian. Tuy nhiên, tôi không thấy làm thế nào có thể trong mọi trường hợp mà không để lộ các thuộc tính TUYỆT VỜI cho thử nghiệm. Lấy ví dụ của tôi ở trên. Tôi không chắc chắn làm thế nào để kiểm tra kho dữ liệu thực sự được gọi mà không sử dụng xác nhận cho "MustHaveBeenCalled". Đối với điểm 2, bạn hoàn toàn chính xác. Tôi đã hoàn thành việc đó sau tất cả các chỉnh sửa, nhưng tôi chỉ muốn chắc chắn rằng cách tiếp cận của tôi nói chung phù hợp với thực tiễn TDD được chấp nhận. Cảm ơn!
Landon

@Landon Có những trường hợp kiểm tra tương tác phù hợp hơn. Ví dụ: xác minh rằng một cuộc gọi đã được thực hiện cho cơ sở dữ liệu hoặc dịch vụ web. Về cơ bản, bất cứ khi nào bạn cần cách ly bài kiểm tra của mình, đặc biệt là từ một dịch vụ bên ngoài.
jhewlett

@Landon Tôi là "người theo chủ nghĩa cổ điển thuyết phục", vì vậy tôi không có nhiều kinh nghiệm với thử nghiệm dựa trên sự tương tác ... Nhưng bạn không cần phải đưa ra một khẳng định cho "MustHaveBeenCalled". Nếu bạn đang kiểm tra chèn, bạn có thể sử dụng truy vấn để xem liệu nó có được chèn không. PS: Tôi sử dụng sơ khai do cân nhắc hiệu năng khi kiểm tra mọi thứ trừ lớp cơ sở dữ liệu.
Hbas

@jhewlett Đó là kết luận mà tôi đã đến. Cảm ơn!
Landon

@Hbas Không có cơ sở dữ liệu để truy vấn. Tôi đồng ý rằng đó sẽ là con đường thẳng nhất để đi nếu tôi có một cái, nhưng tôi đang thêm nó vào sổ ghi chép OneNote. Cách tốt nhất tôi có thể làm thay vào đó là thêm một phương thức Get vào lớp trình trợ giúp xen kẽ của tôi để cố gắng kéo trang. TÔI CÓ THỂ viết bài kiểm tra để làm điều đó, nhưng tôi cảm thấy như tôi đang thử nghiệm hai điều cùng một lúc: Tôi có lưu cái này không? và lớp người trợ giúp của tôi có truy xuất chính xác các trang không? Mặc dù, tôi đoán tại một số điểm, các thử nghiệm của bạn có thể phải dựa vào mã khác được thử nghiệm ở nơi khác. Cảm ơn!
Landon

10

... Tôi không thể không cảm thấy mình sẽ nắm bắt được điều này nhanh hơn nếu tôi phác thảo thiết kế trước thời hạn thay vì để nó được thiết kế trong quá trình TDD ...

Co le không

Một mặt, TDD hoạt động tốt, cung cấp cho bạn các bài kiểm tra tự động khi bạn xây dựng chức năng và ngay lập tức bị hỏng khi bạn phải thay đổi giao diện.

Mặt khác, có lẽ nếu bạn đã bắt đầu với tính năng cấp cao (SaveProject) thay vì tính năng cấp thấp hơn (CreatProject), bạn sẽ nhận thấy các tham số bị thiếu sớm hơn.

Sau đó, một lần nữa, có thể bạn sẽ không có. Đó là một thử nghiệm không thể lặp lại.

Nhưng nếu bạn đang tìm kiếm một bài học cho lần tới: hãy bắt đầu từ đầu. Và nghĩ về thiết kế nhiều như bạn muốn đầu tiên.


0

https://frontendmasters.com/cifts/angularjs-and-code-testability/ Từ khoảng 2:22:00 đến hết (khoảng 1 giờ). Xin lỗi vì video không miễn phí, nhưng tôi chưa tìm thấy video miễn phí giải thích nó tốt như vậy.

Một trong những bài thuyết trình tốt nhất về viết mã kiểm tra là trong bài học này. Đó là một lớp AngularJS, nhưng phần kiểm thử chỉ xoay quanh mã java, chủ yếu là vì những gì anh ta đang nói không liên quan gì đến ngôn ngữ và mọi thứ phải làm với việc viết mã có thể kiểm tra tốt ngay từ đầu.

Điều kỳ diệu là viết mã kiểm tra, thay vì viết mã kiểm tra. Đây không phải là về việc viết mã mà giả vờ là một người dùng.

Ông cũng dành thời gian để viết thông số kỹ thuật dưới dạng xác nhận kiểm tra.

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.