Khi nào bạn viết mã thực tế của người Viking trong TDD?


147

Tất cả các ví dụ tôi đã đọc và thấy trên các video đào tạo đều có các ví dụ đơn giản. Nhưng những gì tôi không thấy nếu tôi làm mã "thực" sau khi tôi nhận được màu xanh lá cây. Đây có phải là phần "Tái cấu trúc" không?

Nếu tôi có một đối tượng khá phức tạp với một phương thức phức tạp và tôi viết thử nghiệm của mình và mức tối thiểu để làm cho nó vượt qua (sau khi nó thất bại lần đầu, Red). Khi nào tôi quay lại và viết mã thật? Và tôi viết bao nhiêu mã thật trước khi kiểm tra lại? Tôi đoán rằng cái cuối cùng là trực giác hơn.

Chỉnh sửa: Cảm ơn tất cả những người đã trả lời. Tất cả các câu trả lời của bạn đã giúp tôi rất nhiều. Dường như có những ý tưởng khác nhau về những gì tôi đã hỏi hoặc nhầm lẫn, và có thể có, nhưng những gì tôi đã hỏi là, nói rằng tôi có một ứng dụng để xây dựng một trường học.

Trong thiết kế của tôi, tôi có một kiến ​​trúc mà tôi muốn bắt đầu, Câu chuyện người dùng, v.v. Từ đây, tôi lấy những Câu chuyện người dùng đó và tôi tạo một bài kiểm tra để kiểm tra Câu chuyện người dùng. Người dùng nói, Chúng tôi có người đăng ký học và trả phí đăng ký. Vì vậy, tôi nghĩ ra một cách để làm cho thất bại đó. Để làm như vậy, tôi thiết kế một lớp kiểm tra cho lớp X (có thể là Sinh viên), sẽ thất bại. Sau đó tôi tạo lớp "Học sinh." Có lẽ "Trường học" tôi không biết.

Nhưng, trong mọi trường hợp, Thiết kế TD đang buộc tôi phải suy nghĩ thông qua câu chuyện. Nếu tôi có thể làm một bài kiểm tra thất bại, tôi biết tại sao nó thất bại, nhưng điều này giả định tôi có thể làm cho nó vượt qua. Đó là về thiết kế.

Tôi thích điều này để suy nghĩ về đệ quy. Đệ quy không phải là một khái niệm khó. Có thể khó theo dõi nó trong đầu hơn, nhưng trong thực tế, phần khó nhất là khi biết, khi đệ quy "phá vỡ", khi nào thì dừng lại (tất nhiên là ý kiến ​​của tôi.) Vì vậy, tôi phải suy nghĩ về những gì dừng lại Đệ quy đầu tiên. Nó chỉ là một sự tương tự không hoàn hảo, và nó giả định rằng mỗi lần lặp đệ quy là một "vượt qua". Một lần nữa, chỉ là một ý kiến.

Trong thực hiện, Trường khó nhìn thấy hơn. Sổ cái số và ngân hàng là "dễ dàng" theo nghĩa bạn có thể sử dụng số học đơn giản. Tôi có thể thấy a + b và trả về 0, v.v ... Trong trường hợp hệ thống của mọi người, tôi phải suy nghĩ kỹ hơn về cách thực hiện điều đó. Tôi có khái niệm về sự thất bại, vượt qua, tái cấu trúc (chủ yếu là vì nghiên cứu và câu hỏi này.)

Theo tôi, những gì tôi không biết là dựa trên sự thiếu kinh nghiệm. Tôi không biết làm thế nào để không đăng ký một sinh viên mới. Tôi không biết làm thế nào để thất bại ai đó gõ tên cuối cùng và nó được lưu vào cơ sở dữ liệu. Tôi biết cách tạo +1 cho toán học đơn giản, nhưng với các thực thể như một người, tôi không biết liệu tôi chỉ thử nghiệm để xem liệu tôi có lấy lại được ID duy nhất của cơ sở dữ liệu hay thứ gì khác không khi ai đó nhập tên vào cơ sở dữ liệu hoặc cả hai hoặc không.

Hoặc, có thể điều này cho thấy tôi vẫn còn bối rối.


193
Sau khi người TDD về nhà nghỉ đêm.
hobbs

14
Tại sao bạn nghĩ mã bạn viết không có thật?
Goyo

2
@RubberDuck Nhiều hơn những câu trả lời khác đã làm. Tôi chắc chắn tôi sẽ đề cập đến nó sớm. Nó vẫn là loại nước ngoài, nhưng tôi sẽ không từ bỏ nó. Những gì bạn nói có ý nghĩa. Tôi chỉ đang cố gắng làm cho nó có ý nghĩa trong bối cảnh của tôi hoặc một ứng dụng kinh doanh thông thường. Có thể là một hệ thống hàng tồn kho hoặc tương tự. Tôi phải xem xét nó. Tôi biết ơn thời gian của bạn mặc dù. Cảm ơn.
johnny

1
Các câu trả lời đã đánh vào đầu, nhưng miễn là tất cả các bài kiểm tra của bạn đều trôi qua và bạn không cần bất kỳ bài kiểm tra / chức năng mới nào, có thể giả sử mã bạn đã hoàn thành, thanh màu.
ESR

3
Có một giả định trong câu hỏi có thể có vấn đề trong "Tôi có một đối tượng khá phức tạp với một phương thức phức tạp". Trong TDD, bạn viết các bài kiểm tra của mình trước để bạn bắt đầu với một mã khá đơn giản. Điều này sẽ buộc bạn phải mã hóa một cấu trúc thân thiện với thử nghiệm sẽ cần phải được mô đun hóa. Vì vậy, hành vi phức tạp sẽ được tạo ra bằng cách kết hợp các đối tượng đơn giản hơn. Nếu bạn kết thúc với một đối tượng hoặc phương thức khá phức tạp thì đó là khi bạn tái cấu trúc
Borjab 26/07/17

Câu trả lời:


243

Nếu tôi có một đối tượng khá phức tạp với một phương thức phức tạp và tôi viết thử nghiệm của mình và mức tối thiểu để làm cho nó vượt qua (sau khi nó thất bại lần đầu, Red). Khi nào tôi quay lại và viết mã thật? Và tôi viết bao nhiêu mã thật trước khi kiểm tra lại? Tôi đoán rằng cái cuối cùng là trực giác hơn.

Bạn không "quay lại" và viết "mã thực". Tất cả đều là mã thật. Những gì bạn làm là quay lại và thêm một bài kiểm tra khác buộc bạn phải thay đổi mã của mình để thực hiện bài kiểm tra mới.

Bạn viết bao nhiêu mã trước khi thi lại? Không ai. Bạn viết mã bằng 0 mà không có bài kiểm tra thất bại buộc bạn phải viết thêm mã.

Chú ý mẫu?

Chúng ta hãy đi qua (một) ví dụ đơn giản khác với hy vọng rằng nó sẽ giúp.

Assert.Equal("1", FizzBuzz(1));

Dễ đậu.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Không phải những gì bạn sẽ gọi mã thực sự, phải không? Hãy thêm một bài kiểm tra buộc thay đổi.

Assert.Equal("2", FizzBuzz(2));

Chúng ta có thể làm điều gì đó ngớ ngẩn như thế if n == 1, nhưng chúng ta sẽ bỏ qua giải pháp lành mạnh.

public String FizzBuzz(int n) {
    return n.ToString();
}

Mát mẻ. Điều này sẽ làm việc cho tất cả các số không phải FizzBuzz. Đầu vào tiếp theo sẽ buộc mã sản xuất thay đổi là gì?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

Và một lần nữa. Viết một bài kiểm tra chưa vượt qua.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

Và chúng tôi hiện đã bao gồm tất cả bội số của ba (đó cũng không phải là bội số của năm, chúng tôi sẽ lưu ý và quay lại).

Chúng tôi chưa viết bài kiểm tra cho "Buzz", vì vậy hãy viết nó.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

Và một lần nữa, chúng tôi biết có một trường hợp khác chúng tôi cần xử lý.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

Và bây giờ chúng ta có thể xử lý tất cả bội số của 5 không phải là bội số của 3.

Cho đến thời điểm này, chúng tôi đã bỏ qua bước tái cấu trúc, nhưng tôi thấy một số trùng lặp. Hãy dọn dẹp nó ngay bây giờ.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Mát mẻ. Bây giờ chúng tôi đã loại bỏ trùng lặp và tạo ra một chức năng được đặt tên tốt. Bài kiểm tra tiếp theo chúng ta có thể viết sẽ buộc chúng ta thay đổi mã là gì? Chà, chúng tôi đã tránh trường hợp số đó chia hết cho cả 3 và 5. Hãy viết nó ngay bây giờ.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Các bài kiểm tra vượt qua, nhưng chúng tôi có nhiều sự trùng lặp. Chúng tôi có các tùy chọn, nhưng tôi sẽ áp dụng "Trích xuất biến cục bộ" một vài lần để chúng tôi tái cấu trúc thay vì viết lại.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Và chúng tôi đã bao gồm mọi đầu vào hợp lý, nhưng còn đầu vào không hợp lý thì sao? Điều gì xảy ra nếu chúng ta vượt qua 0 hoặc âm? Viết những trường hợp thử nghiệm.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Đây có phải là bắt đầu giống như "mã thực sự" chưa? Quan trọng hơn, tại thời điểm nào nó đã ngừng là "mã không thực" và chuyển sang "thực"? Đó là điều đáng suy ngẫm về ...

Vì vậy, tôi đã có thể làm điều này chỉ bằng cách tìm kiếm một bài kiểm tra mà tôi biết sẽ không vượt qua ở mỗi bước, nhưng tôi đã có rất nhiều thực hành. Khi tôi ở nơi làm việc, mọi thứ không đơn giản như vậy và tôi có thể không phải lúc nào cũng biết thử nghiệm nào sẽ buộc thay đổi. Đôi khi tôi sẽ viết một bài kiểm tra và ngạc nhiên khi thấy nó đã qua! Tôi thực sự khuyên bạn nên tập thói quen tạo "Danh sách kiểm tra" trước khi bắt đầu. Danh sách kiểm tra này nên chứa tất cả các đầu vào "thú vị" mà bạn có thể nghĩ đến. Bạn có thể không sử dụng tất cả chúng và bạn có thể sẽ thêm các trường hợp khi bạn đi, nhưng danh sách này đóng vai trò là một lộ trình. Danh sách thử nghiệm của tôi cho FizzBuzz sẽ trông giống như thế này.

  • Tiêu cực
  • Số không
  • Một
  • Hai
  • Số ba
  • Bốn
  • Số năm
  • Sáu (bội số không tầm thường của 3)
  • Chín (3 bình phương)
  • Mười (bội số không tầm thường của 5)
  • 15 (bội số của 3 & 5)
  • 30 (bội số không nhỏ của 3 & 5)

3
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
maple_shaft

47
Trừ khi tôi hoàn toàn hiểu sai câu trả lời này: "Chúng tôi có thể làm điều gì đó ngớ ngẩn như nếu n == 1, nhưng chúng tôi sẽ bỏ qua giải pháp lành mạnh." - toàn bộ điều là ngớ ngẩn. Nếu bạn biết trước bạn muốn có một chức năng <spec>, hãy viết các bài kiểm tra cho <spec> và bỏ qua phần mà bạn viết các phiên bản rõ ràng không thành công <spec>. Nếu bạn tìm thấy một lỗi trong <spec> thì chắc chắn: hãy viết một bài kiểm tra trước để xác minh bạn có thể thực hiện nó trước khi sửa lỗi và quan sát các bài kiểm tra sau khi sửa lỗi. Nhưng không cần phải giả tất cả các bước trung gian này.
GManNickG

16
Các ý kiến ​​chỉ ra những sai sót lớn trong câu trả lời này và TDD nói chung đã được chuyển sang trò chuyện. Nếu bạn đang cân nhắc sử dụng TDD, vui lòng đọc 'trò chuyện'. Thật không may, các bình luận 'chất lượng' hiện bị ẩn trong vô số trò chuyện để các sinh viên tương lai đọc.
dùng3791372

2
@GManNickG Tôi tin rằng vấn đề là làm đúng số lượng bài kiểm tra. Viết các bài kiểm tra trước giúp bạn dễ dàng bỏ lỡ những trường hợp đặc biệt nào cần được kiểm tra, dẫn đến tình huống không được bảo hiểm đầy đủ trong các bài kiểm tra, hoặc về cơ bản là tình huống tương tự được bao phủ vô số lần trong các bài kiểm tra. Nếu bạn có thể làm điều đó mà không cần các bước trung gian này, thật tuyệt! Tuy nhiên, không phải ai cũng có thể làm như vậy, đó là điều cần thực hành.
hvd

1
Và đây là một câu trích dẫn của Kent Beck về tái cấu trúc: "Bây giờ khi thử nghiệm chạy, chúng ta có thể nhận ra (như trong cách tạo ra thực tế ) và việc thực hiện tóm tắt ()". Sau đó, anh ta tiến hành thay đổi một hằng số thành một biến. Tôi cảm thấy trích dẫn này phù hợp với câu hỏi khá tốt.
Chris Wohlert

46

Mã "thực" là mã bạn viết để vượt qua bài kiểm tra của mình. Thực sự . Nó đơn giản mà.

Khi mọi người nói về việc viết mức tối thiểu để làm cho bài kiểm tra màu xanh lá cây, điều đó chỉ có nghĩa là mã thực của bạn phải tuân theo nguyên tắc YAGNI .

Ý tưởng của bước tái cấu trúc chỉ là làm sạch những gì bạn đã viết một khi bạn hài lòng rằng nó đáp ứng các yêu cầu.

Miễn là các bài kiểm tra mà bạn viết thực sự bao gồm các yêu cầu sản phẩm của bạn, một khi chúng được thông qua thì mã hoàn tất. Hãy nghĩ về nó, nếu tất cả các yêu cầu kinh doanh của bạn có một bài kiểm tra và tất cả các bài kiểm tra đó đều có màu xanh, thì còn gì để viết nữa không? (Được rồi, trong cuộc sống thực, chúng ta không có xu hướng kiểm tra toàn diện, nhưng lý thuyết là âm thanh.)


45
Các bài kiểm tra đơn vị thực sự không thể bao gồm các yêu cầu sản phẩm của bạn cho các yêu cầu tương đối tầm thường. Tốt nhất, họ lấy mẫu không gian đầu vào-đầu ra và ý tưởng là bạn (chính xác) khái quát thành không gian đầu vào-đầu ra đầy đủ. Tất nhiên, mã của bạn có thể chỉ là một switchtrường hợp lớn đối với mỗi trường hợp thử nghiệm đơn vị sẽ vượt qua tất cả các thử nghiệm và thất bại đối với bất kỳ đầu vào nào khác.
Derek Elkins

8
@DerekElkins TDD bắt buộc thi trượt. Không thất bại bài kiểm tra đơn vị.
Taemyr

6
@DerekElkins đó là lý do tại sao bạn không chỉ viết bài kiểm tra đơn vị, và cũng là lý do tại sao có một giả định chung rằng bạn đang cố gắng tạo ra thứ gì đó không chỉ là giả!
jonrsharpe

36
@jonrsharpe Theo logic đó, tôi sẽ không bao giờ viết các triển khai tầm thường. Ví dụ, trong ví dụ FizzBuzz trong câu trả lời của RubberDuck (chỉ sử dụng các bài kiểm tra đơn vị), việc thực hiện đầu tiên rõ ràng là "chỉ giả mạo". Sự hiểu biết của tôi về câu hỏi chính xác là sự phân đôi giữa mã viết mà bạn biết là không đầy đủ và mã mà bạn thực sự tin rằng sẽ thực hiện yêu cầu, "mã thực". "Lớn" của tôi switchđược dự định là một cực đoan logic của "viết mức tối thiểu để làm cho các bài kiểm tra màu xanh lá cây". Tôi xem câu hỏi của OP là: TDD ở đâu là nguyên tắc để tránh điều này lớn switch?
Derek Elkins

2
@GenericJon Đó là một chút quá lạc quan trong kinh nghiệm của tôi :) Đối với một người, có những người thích làm việc lặp đi lặp lại không suy nghĩ. Họ sẽ hạnh phúc hơn với một tuyên bố chuyển đổi khổng lồ hơn là với "việc ra quyết định phức tạp". Và để mất việc, họ sẽ cần một người gọi họ về kỹ thuật (và tốt hơn là họ có bằng chứng tốt thực sự là mất cơ hội / tiền của công ty!), Hoặc làm rất tệ. Sau khi tiếp quản bảo trì cho nhiều dự án như vậy, tôi có thể nói rằng thật dễ dàng để mã rất ngây thơ tồn tại trong nhiều thập kỷ, miễn là nó làm cho khách hàng hài lòng (và trả tiền).
Luaan

14

Câu trả lời ngắn gọn là "mã thực" là mã làm cho bài kiểm tra vượt qua. Nếu bạn có thể thực hiện bài kiểm tra của mình bằng một cái gì đó không phải là mã thực, hãy thêm nhiều bài kiểm tra!

Tôi đồng ý rằng rất nhiều hướng dẫn về TDD rất đơn giản. Điều đó chống lại họ. Một thử nghiệm quá đơn giản cho một phương pháp, tính toán 3 + 8 thực sự không có lựa chọn nào khác ngoài việc tính toán 3 + 8 và so sánh kết quả. Điều đó làm cho nó trông giống như bạn sẽ sao chép toàn bộ mã và việc kiểm tra là vô ích, dễ bị lỗi.

Khi bạn giỏi kiểm tra, điều đó sẽ thông báo cho bạn cách cấu trúc ứng dụng của bạn và cách bạn viết mã. Nếu bạn gặp khó khăn khi đưa ra các bài kiểm tra hợp lý, hữu ích, có lẽ bạn nên suy nghĩ lại về thiết kế của mình một chút. Một hệ thống được thiết kế tốt rất dễ kiểm tra - có nghĩa là các thử nghiệm hợp lý rất dễ nghĩ và thực hiện.

Khi bạn viết bài kiểm tra của mình trước, hãy xem chúng thất bại và sau đó viết mã khiến chúng vượt qua, đó là một quy tắc để đảm bảo rằng tất cả mã của bạn có các bài kiểm tra tương ứng. Tôi không tuân theo quy tắc đó khi tôi đang viết mã; Tôi thường viết các bài kiểm tra sau khi thực tế. Nhưng làm các bài kiểm tra đầu tiên giúp giữ cho bạn trung thực. Với một số kinh nghiệm, bạn sẽ bắt đầu chú ý khi bạn tự viết mã vào một góc, ngay cả khi bạn không viết bài kiểm tra trước.


6
Cá nhân, bài kiểm tra tôi sẽ viết assertEqual(plus(3,8), 11), không assertEqual(plus(3,8), my_test_implementation_of_addition(3,8)). Đối với các trường hợp phức tạp hơn, bạn luôn tìm kiếm một phương tiện để chứng minh kết quả chính xác, ngoài việc tính toán linh hoạt kết quả chính xác trong kiểm tra và kiểm tra sự bằng nhau.
Steve Jessop

Vì vậy, một cách thực sự ngớ ngẩn của nó làm cho ví dụ này, bạn có thể chứng minh rằng plus(3,8)đã trở lại kết quả chính xác bằng cách trừ đi 3 từ nó, trừ 8 từ đó, và kiểm tra kết quả chống lại 0. Điều này là rất rõ ràng tương đương assertEqual(plus(3,8), 3+8)như là một bit vô lý, nhưng nếu mã đang thử nghiệm đang xây dựng một cái gì đó phức tạp hơn chỉ là một số nguyên, thì lấy kết quả và kiểm tra từng phần cho chính xác thường là cách tiếp cận đúng. Ngoài ra, một cái gì đó nhưfor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
Steve Jessop

... Vì điều đó tránh được nỗi sợ hãi lớn, đó là khi viết bài kiểm tra, chúng tôi sẽ mắc lỗi tương tự về chủ đề "cách thêm 10" mà chúng tôi đã thực hiện trong mã trực tiếp. Vì vậy, kiểm tra cẩn thận tránh viết bất kỳ mã nào thêm 10 vào bất cứ điều gì, trong thử nghiệm plus()có thể thêm 10 vào mọi thứ. Tất nhiên, chúng tôi vẫn dựa vào các giá trị vòng lặp intial được xác minh bởi lập trình viên.
Steve Jessop

4
Chỉ muốn chỉ ra rằng ngay cả khi bạn đang viết bài kiểm tra sau khi thực tế, vẫn nên xem chúng thất bại; tìm một phần mã có vẻ quan trọng đối với bất cứ điều gì bạn đang làm việc, chỉnh sửa nó một chút (ví dụ thay thế dấu + bằng - hoặc bất cứ thứ gì), chạy thử nghiệm và xem chúng thất bại, hoàn tác thay đổi và xem chúng vượt qua. Đã nhiều lần tôi thực hiện thử nghiệm này không thực sự thất bại, làm cho nó tồi tệ hơn vô dụng: không chỉ là nó không thử nghiệm bất cứ điều gì, nó còn cho tôi niềm tin sai lầm rằng một cái gì đó đang được thử nghiệm!
Warbo

6

Đôi khi một số ví dụ về TDD có thể gây hiểu nhầm. Như những người khác đã chỉ ra trước đó, mã bạn viết để thực hiện các bài kiểm tra là mã thực.

Nhưng đừng nghĩ rằng mã thực sự xuất hiện như ma thuật - đó là sai. Bạn cần hiểu rõ hơn về những gì bạn muốn đạt được và sau đó bạn cần chọn bài kiểm tra phù hợp, bắt đầu từ các trường hợp dễ nhất và các trường hợp góc.

Ví dụ: nếu bạn cần viết một từ vựng, bạn bắt đầu bằng chuỗi rỗng, sau đó với một loạt các khoảng trắng, sau đó là một số, sau đó với một số được bao quanh bởi các khoảng trắng, sau đó là một số sai, v.v ... Những biến đổi nhỏ này sẽ dẫn bạn đến thuật toán đúng, nhưng bạn không chuyển từ trường hợp dễ nhất sang trường hợp cực kỳ phức tạp được chọn một cách ngu ngốc để hoàn thành mã thực.

Bob Martin giải thích nó hoàn hảo ở đây .


5

Phần tái cấu trúc được dọn dẹp khi bạn mệt mỏi và muốn về nhà.

Khi bạn sắp thêm một tính năng, phần cấu trúc lại là phần bạn thay đổi trước lần kiểm tra tiếp theo. Bạn cấu trúc lại mã để nhường chỗ cho tính năng mới. Bạn làm điều này khi bạn biết tính năng mới đó sẽ là gì. Không phải khi bạn chỉ đang tưởng tượng nó.

Điều này có thể đơn giản như đổi tên GreetImplthành GreetWorldtrước khi bạn tạo một GreetMomlớp (sau khi thêm bài kiểm tra) để thêm một tính năng sẽ in "Chào mẹ".


1

Nhưng mã thực sẽ xuất hiện trong giai đoạn tái cấu trúc của pha TDD. Tức là mã nên là một phần của bản phát hành cuối cùng.

Kiểm tra nên được chạy mỗi khi bạn thực hiện một thay đổi.

Phương châm của vòng đời TDD sẽ là: NHÀ XÂY DỰNG XANH ĐỎ

ĐỎ : Viết các bài kiểm tra

MÀU XANH LÁ : Thực hiện một nỗ lực trung thực để có được mã chức năng vượt qua các bài kiểm tra càng nhanh càng tốt: mã trùng lặp, các biến được đặt tên khó hiểu của các đơn hàng cao nhất, v.v.

REFACTOR : Làm sạch mã, đặt tên đúng cho các biến. DRY lên mã.


6
Tôi biết những gì bạn đang nói về giai đoạn "Xanh" nhưng nó ngụ ý rằng các giá trị trả về dây cứng để thực hiện các bài kiểm tra có thể phù hợp. Theo kinh nghiệm của tôi, "Green" phải là một nỗ lực trung thực để tạo mã làm việc để đáp ứng yêu cầu, nó có thể không hoàn hảo nhưng nó phải hoàn chỉnh và "có thể chuyển đổi" như nhà phát triển có thể quản lý trong lần đầu tiên. Tái cấu trúc có lẽ được thực hiện tốt nhất một thời gian sau khi bạn đã phát triển nhiều hơn và các vấn đề với lần đầu tiên trở nên rõ ràng hơn và cơ hội để DRY xuất hiện.
đánh dấu

2
@mcottle: bạn có thể ngạc nhiên khi có bao nhiêu triển khai của kho lưu trữ chỉ có thể là các giá trị được mã hóa cứng trong cơ sở mã. :)
Bryan Boettcher

6
Tại sao tôi lại viết mã tào lao và dọn sạch nó, khi tôi có thể tạo ra mã đẹp, chất lượng sản xuất gần như nhanh như tôi có thể gõ? :)
Kaz

1
@Kaz Vì cách này bạn có nguy cơ thêm hành vi chưa được kiểm tra . Cách duy nhất để đảm bảo có bài kiểm tra cho từng hành vi mong muốn là thực hiện các mô phỏng có thể thay đổi bất kể nó tệ đến mức nào. Đôi khi việc tái cấu trúc sau đây mang đến một cách tiếp cận mới mà bạn không nghĩ đến trước ...
Timothy Truckle

1
@TimothyTruckle Điều gì nếu mất 50 phút để tìm thay đổi đơn giản nhất có thể, nhưng chỉ 5 để tìm thay đổi đơn giản thứ hai có thể? Chúng ta đi với đơn giản thứ hai hoặc tiếp tục tìm kiếm đơn giản nhất?
Kaz

1

Khi nào bạn viết mã thực tế của người Viking trong TDD?

Các màu đỏ pha là nơi bạn viết mã.

Trong giai đoạn tái cấu trúc , mục tiêu chính là xóa mã.

Trong giai đoạn đỏ, bạn làm bất cứ điều gì để vượt qua bài kiểm tra nhanh nhất có thểbằng mọi giá . Bạn hoàn toàn không quan tâm đến những gì bạn đã từng nghe về các thực hành mã hóa tốt hoặc mẫu thiết kế giống nhau. Làm cho thử nghiệm màu xanh lá cây là tất cả những gì quan trọng.

Trong giai đoạn tái cấu trúc, bạn dọn dẹp mớ hỗn độn bạn vừa làm. Bây giờ, trước tiên bạn hãy xem liệu thay đổi bạn vừa thực hiện là loại hàng đầu nhất trong danh sách Ưu tiên chuyển đổi và nếu có bất kỳ sao chép mã nào, bạn có thể loại bỏ nhiều khả năng bằng cách áp dụng một thiết kế.

Cuối cùng, bạn cải thiện khả năng đọc bằng cách đổi tên định danh và trích xuất số ma thuật và / hoặc chuỗi ký tự thành hằng số.


Nó không phải là refactor đỏ, nó là red-green-refactor. - Cướp Kinyon

Cảm ơn đã chỉ vào điều này.

Vì vậy, đó là giai đoạn màu xanh lá cây nơi bạn viết mã thực

Trong pha màu đỏ, bạn viết đặc tả thực thi ...


Nó không phải là refactor đỏ, nó là red-green-refactor. "Màu đỏ" là bạn đưa bộ kiểm tra của mình từ màu xanh lá cây (tất cả các bài kiểm tra vượt qua) sang màu đỏ (một bài kiểm tra thất bại). "Màu xanh lá cây" là nơi bạn nhanh chóng đưa bộ thử nghiệm của mình từ màu đỏ (một thử nghiệm thất bại) sang màu xanh lá cây (tất cả các thử nghiệm đều vượt qua). "Refactor" là nơi bạn lấy mã của mình và làm cho nó đẹp hơn trong khi giữ cho tất cả các thử nghiệm đi qua.
Rob Kinyon

1

Bạn đang viết Real Code toàn bộ thời gian.

Ở mỗi bước Bạn đang viết mã để đáp ứng các điều kiện mà Mã của bạn sẽ đáp ứng cho những người gọi mã của bạn trong tương lai (có thể là Bạn hoặc không ...).

Bạn nghĩ rằng bạn không viết mã ( thật ) hữu ích , bởi vì trong một khoảnh khắc, bạn có thể cấu trúc lại nó.

Tái cấu trúc mã là quá trình tái cấu trúc mã máy tính hiện tại, Thay đổi bao thanh toán mà không thay đổi hành vi bên ngoài của nó.

Điều này có nghĩa là mặc dù Bạn đang thay đổi mã, các điều kiện mà mã đã được bão hòa, vẫn không thay đổi. Và kiểm tra ( kiểm tra ) Bạn đã triển khai để xác minh Mã của bạn đã ở đó để xác minh xem các sửa đổi của bạn có thay đổi gì không. Vì vậy, mã Bạn đã viết toàn bộ thời gian là ở đó, theo một cách khác.

Một lý do khác Bạn có thể nghĩ rằng đó không phải là mã thực, đó là Bạn đang thực hiện các ví dụ trong đó chương trình kết thúc có thể được Bạn thấy trước. Đây là rất tốt, vì nó cho thấy bạn có kiến thức về miền Bạn đang lập trình trong.
Tuy nhiên, nhiều lần lập trình viên đang ở trong một miền mà là mới , chưa biết đối với họ. Họ không biết kết quả cuối cùng sẽ ra sao và TDD là một kỹ thuật để viết chương trình từng bước, ghi lại kiến thức của chúng tôi về cách hệ thống này sẽ hoạt động và xác minh rằng mã của chúng tôi hoạt động theo cách đó.

Khi tôi đọc Sách (*) trên TDD, đối với tôi, tính năng quan trọng nhất nổi bật là: danh sách TODO. Nó cho tôi thấy rằng, TDD cũng là một kỹ thuật để giúp các nhà phát triển tập trung vào một việc tại một thời điểm. Vì vậy, đây cũng là một câu trả lời cho câu hỏi của bạn aboout Viết mã Real bao nhiêu ? Tôi sẽ nói đủ mã để tập trung vào 1 điều tại một thời điểm.

(*) "Phát triển dựa trên thử nghiệm: Ví dụ" của Kent Beck


2
"Phát triển dựa trên thử nghiệm: Ví dụ" của Kent Beck
Robert Andrzejuk 27/07/17

1

Bạn không viết mã để làm bài kiểm tra của bạn thất bại.

Bạn viết các bài kiểm tra của mình để xác định thành công sẽ như thế nào, tất cả ban đầu sẽ thất bại vì bạn chưa viết mã sẽ vượt qua.

Toàn bộ quan điểm về việc viết các bài kiểm tra thất bại ban đầu là làm hai việc:

  1. Bao gồm tất cả các trường hợp - tất cả các trường hợp danh nghĩa, tất cả các trường hợp cạnh, vv
  2. Xác nhận bài kiểm tra của bạn. Nếu bạn chỉ nhìn thấy họ vượt qua, làm thế nào bạn có thể chắc chắn rằng họ sẽ báo cáo thất bại một cách đáng tin cậy khi xảy ra?

Điểm đằng sau công cụ tái cấu trúc màu đỏ-xanh là việc viết các bài kiểm tra chính xác trước tiên giúp bạn tự tin để biết rằng mã bạn đã viết để vượt qua các bài kiểm tra là chính xác và cho phép bạn tái cấu trúc với sự tự tin rằng các bài kiểm tra của bạn sẽ thông báo cho bạn ngay khi một cái gì đó bị hỏng, vì vậy bạn có thể ngay lập tức quay lại và sửa nó.

Theo kinh nghiệm của riêng tôi (C # /. NET), thử nghiệm thuần túy đầu tiên là một chút lý tưởng không thể đạt được, bởi vì bạn không thể biên dịch một cuộc gọi đến một phương thức chưa tồn tại. Vì vậy, "thử nghiệm đầu tiên" thực sự là về việc mã hóa các giao diện và triển khai thực thi trước, sau đó viết các thử nghiệm đối với các sơ khai (ban đầu sẽ thất bại) cho đến khi các sơ khai được xác thực. Tôi chưa bao giờ viết "mã thất bại", chỉ xây dựng từ sơ khai.


0

Tôi nghĩ rằng bạn có thể bị nhầm lẫn giữa các bài kiểm tra đơn vị và kiểm tra tích hợp. Tôi tin rằng cũng có thể có các bài kiểm tra chấp nhận, nhưng điều đó phụ thuộc vào quá trình của bạn.

Khi bạn đã kiểm tra tất cả các "đơn vị" nhỏ, sau đó bạn kiểm tra tất cả chúng được lắp ráp hoặc "tích hợp". Đó thường là toàn bộ chương trình hoặc thư viện.

Trong mã mà tôi đã viết tích hợp kiểm tra một thư viện với các chương trình kiểm tra khác nhau đọc dữ liệu và đưa nó vào thư viện, sau đó kiểm tra kết quả. Sau đó, tôi làm điều đó với chủ đề. Sau đó, tôi làm điều đó với chủ đề và ngã ba () ở giữa. Sau đó, tôi chạy nó và giết -9 sau 2 giây, sau đó tôi khởi động nó và kiểm tra chế độ phục hồi của nó. Tôi đánh lừa nó. Tôi tra tấn nó bằng đủ mọi cách.

Tất cả điều đó là thử nghiệm CSONG, nhưng tôi không có màn hình màu đỏ / xanh lá cây cho kết quả. Nó thành công hoặc tôi đào qua vài nghìn dòng mã lỗi để tìm hiểu lý do tại sao.

Đó là nơi bạn kiểm tra "mã thực."

Và tôi chỉ nghĩ về điều này, nhưng có lẽ bạn không biết khi nào bạn nên hoàn thành việc viết bài kiểm tra đơn vị. Bạn đã hoàn thành việc viết bài kiểm tra đơn vị khi bài kiểm tra của bạn thực hiện mọi thứ mà bạn đã chỉ định. Đôi khi bạn có thể mất dấu vết của tất cả các trường hợp xử lý lỗi và các trường hợp cạnh, vì vậy bạn có thể muốn tạo một nhóm thử nghiệm tốt cho các thử nghiệm đường dẫn hạnh phúc mà chỉ cần đi thẳng qua các thông số kỹ thuật.


(của nó = sở hữu, nó = "nó là" hoặc "nó có". Xem ví dụ Cách sử dụng và nó .)
Peter Mortensen

-6

Để trả lời cho tiêu đề của câu hỏi: "Khi nào bạn viết mã Real thực sự trong TDD?", Câu trả lời là: "hầu như không bao giờ" hoặc "rất chậm".

Bạn có vẻ như là một học sinh, vì vậy tôi sẽ trả lời như thể tư vấn cho một sinh viên.

Bạn sẽ học được rất nhiều mã hóa 'lý thuyết' và 'kỹ thuật'. Chúng thật tuyệt khi dành thời gian cho các khóa học sinh viên quá đắt, nhưng rất ít lợi ích cho bạn mà bạn không thể đọc trong một cuốn sách trong một nửa thời gian.

Công việc của một lập trình viên chỉ là sản xuất mã. Mã hoạt động thực sự tốt. Đó là lý do tại sao bạn, lập trình viên lập kế hoạch mã trong tâm trí của bạn, trên giấy, trong một ứng dụng phù hợp, v.v., và bạn có kế hoạch xử lý các lỗ hổng / lỗ hổng có thể trước bằng cách suy nghĩ logic và ngang trước khi mã hóa.

Nhưng bạn cần biết cách phá vỡ ứng dụng của mình để có thể thiết kế mã tốt. Ví dụ: nếu bạn không biết về Bảng Little Bobby (xkcd 327), thì có lẽ bạn sẽ không vệ sinh đầu vào của mình trước khi làm việc với cơ sở dữ liệu, vì vậy bạn sẽ không thể bảo mật dữ liệu của mình xung quanh khái niệm đó.

TDD chỉ là một quy trình công việc được thiết kế để giảm thiểu các lỗi trong mã của bạn bằng cách tạo các bài kiểm tra về những gì có thể sai trước khi bạn mã hóa ứng dụng của mình bởi vì mã hóa có thể gặp khó khăn theo cấp số nhân khi bạn giới thiệu nhiều mã hơn và bạn quên các lỗi mà bạn từng nghĩ đến. Khi bạn nghĩ rằng bạn đã hoàn thành ứng dụng của mình, bạn chạy các bài kiểm tra và bùng nổ, hy vọng các lỗi sẽ được bắt gặp với các bài kiểm tra của bạn.

TDD không phải - như một số người tin - viết một bài kiểm tra, vượt qua nó với mã tối thiểu, viết một bài kiểm tra khác, vượt qua nó với mã tối thiểu, v.v. Thay vào đó, đó là cách giúp bạn tự tin viết mã. Lý tưởng này là tái cấu trúc mã liên tục để làm cho nó hoạt động với các bài kiểm tra là ngu ngốc, nhưng đó là một khái niệm hay giữa các sinh viên vì nó khiến họ cảm thấy tuyệt vời khi họ thêm một tính năng mới và họ vẫn đang học cách viết mã ...

Xin đừng rơi vào cái bẫy này và xem vai trò của bạn về mã hóa là gì - công việc của một lập trình viên chỉ là sản xuất mã. Mã hoạt động thực sự tốt. Bây giờ, hãy nhớ rằng bạn sẽ ở trên đồng hồ với tư cách là một lập trình viên chuyên nghiệp và khách hàng của bạn sẽ không quan tâm nếu bạn đã viết 100.000 xác nhận hoặc 0. Họ chỉ muốn mã hoạt động. Thực sự tốt, trong thực tế.


3
Tôi thậm chí không gần gũi với một sinh viên, nhưng tôi đã đọc và cố gắng áp dụng các kỹ thuật tốt và chuyên nghiệp. Vì vậy, theo nghĩa đó, tôi là một "sinh viên." Tôi chỉ hỏi những câu hỏi rất cơ bản bởi vì đó là tôi. Tôi muốn biết chính xác tại sao tôi đang làm những gì tôi đang làm. Trung tâm của vấn đề. Nếu tôi không hiểu điều đó, tôi không thích nó và bắt đầu đặt câu hỏi. Tôi cần biết tại sao, nếu tôi sẽ sử dụng nó. TDD có vẻ tốt về mặt trực giác theo một số cách như biết những gì bạn cần tạo và suy nghĩ mọi thứ thông qua, nhưng việc thực hiện rất khó hiểu. Tôi nghĩ rằng tôi đã nắm bắt tốt hơn bây giờ.
johnny


4
Đó là những quy tắc của TDD. Bạn có thể tự do viết mã theo cách bạn muốn, nhưng nếu bạn không tuân theo ba quy tắc đó thì bạn không làm TDD.
Sean Burton

2
"Quy tắc" của một người? TDD là một gợi ý để giúp bạn viết mã chứ không phải tôn giáo. Thật buồn khi thấy nhiều người tuân thủ một ý tưởng như vậy. Ngay cả nguồn gốc của TDD cũng gây tranh cãi.
dùng3791372

2
@ user3791372 TDD là một quy trình rất nghiêm ngặt và được xác định rõ ràng. Ngay cả khi nhiều người nghĩ rằng điều đó chỉ có nghĩa là "Thực hiện một số thử nghiệm khi bạn đang lập trình", thì không. Chúng ta hãy cố gắng không trộn lẫn các thuật ngữ ở đây, câu hỏi này là về quá trình TDD, không phải thử nghiệm chung.
Alex
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.