Làm thế nào để tránh những lỗi logic trong mã, khi TDD không giúp đỡ?


67

Gần đây tôi đã viết một đoạn mã nhỏ có thể chỉ ra một cách thân thiện với con người bao nhiêu tuổi một sự kiện. Chẳng hạn, nó có thể chỉ ra rằng sự kiện đã xảy ra, cách đây ba tuần, hay cách đây một tháng trước

Các yêu cầu tương đối rõ ràng và đây là một trường hợp hoàn hảo để phát triển theo hướng thử nghiệm. Tôi đã viết từng bài kiểm tra một, thực hiện mã để vượt qua từng bài kiểm tra và mọi thứ dường như hoạt động hoàn hảo. Cho đến khi một lỗi xuất hiện trong sản xuất.

Đây là đoạn mã có liên quan:

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return _number_to_text(delta) + " days ago"

if delta < 30:
    weeks = math.floor(delta / 7)
    if weeks == 1:
        return "A week ago"

    return _number_to_text(weeks) + " weeks ago"

if delta < 365:
    ... # Handle months and years in similar manner.

Các thử nghiệm đã kiểm tra trường hợp của một sự kiện xảy ra hôm nay, hôm qua, bốn ngày trước, hai tuần trước, một tuần trước, v.v., và mã được xây dựng tương ứng.

Điều tôi đã bỏ lỡ là một sự kiện có thể xảy ra một ngày trước ngày hôm qua, trong khi đó là một ngày trước: ví dụ một sự kiện xảy ra hai mươi sáu giờ trước sẽ là một ngày trước, trong khi không chính xác là ngày hôm qua nếu bây giờ là 1 giờ sáng. một cái gì đó, nhưng vì deltalà một số nguyên, nó sẽ chỉ là một. Trong trường hợp này, ứng dụng sẽ hiển thị trên mạng Một ngày trước, rõ ràng là bất ngờ và chưa được xử lý trong mã. Nó có thể được sửa bằng cách thêm:

if delta == 1:
    return "A day ago"

ngay sau khi tính toán delta.

Mặc dù hậu quả tiêu cực duy nhất của lỗi là tôi đã lãng phí nửa giờ để tự hỏi trường hợp này có thể xảy ra như thế nào (và tin rằng nó phải làm với các múi giờ, mặc dù việc sử dụng UTC trong mã thống nhất), sự hiện diện của nó đang gây phiền toái cho tôi. Nó chỉ ra rằng:

  • Rất dễ phạm phải một lỗi logic ngay cả trong một mã nguồn đơn giản như vậy.
  • Phát triển theo hướng kiểm tra đã không giúp đỡ.

Điều đáng lo ngại là tôi không thể thấy làm thế nào có thể tránh được những lỗi như vậy. Bên cạnh suy nghĩ nhiều hơn trước khi viết mã, cách duy nhất tôi có thể nghĩ đến là thêm nhiều khẳng định cho các trường hợp mà tôi tin rằng sẽ không bao giờ xảy ra (như tôi tin rằng một ngày trước nhất thiết phải là ngày hôm qua), và sau đó lặp đi lặp lại mỗi giây mười năm qua, kiểm tra bất kỳ vi phạm khẳng định nào, có vẻ quá phức tạp.

Làm thế nào tôi có thể tránh tạo ra lỗi này ở nơi đầu tiên?


38
Bằng cách có một trường hợp thử nghiệm cho nó? Điều đó có vẻ giống như cách bạn phát hiện ra nó sau đó và chia sẻ với TDD.
urur

63
Bạn vừa mới trải nghiệm lý do tại sao tôi không phải là người hâm mộ phát triển theo hướng thử nghiệm - theo kinh nghiệm của tôi, hầu hết các lỗi bắt gặp trong sản xuất là những tình huống mà không ai nghĩ tới. Kiểm tra hướng phát triển và kiểm tra đơn vị không làm gì cho những điều này. (Tuy nhiên, các thử nghiệm đơn vị có giá trị trong việc phát hiện các lỗi được giới thiệu thông qua các chỉnh sửa trong tương lai.)
Loren Pechtel

102
Lặp lại sau tôi: "Không có viên đạn bạc, kể cả TDD." Không có quy trình, không có bộ quy tắc, không có thuật toán nào bạn có thể làm theo một cách robot để tạo ra mã hoàn hảo. Nếu có, chúng tôi có thể tự động hóa toàn bộ quá trình và được thực hiện với nó.
jpmc26

43
Xin chúc mừng, bạn đã khám phá lại sự khôn ngoan cũ mà không có bài kiểm tra nào có thể chứng minh sự vắng mặt của bọ. Nhưng nếu bạn đang tìm kiếm các kỹ thuật để tạo độ bao phủ tốt hơn cho miền đầu vào có thể, bạn cần phân tích kỹ lưỡng về miền, các trường hợp cạnh và các lớp tương đương của miền đó. Tất cả các kỹ thuật cũ, nổi tiếng đã được biết đến từ lâu trước khi thuật ngữ TDD được phát minh.
Doc Brown

80
Tôi không cố gắng để trở nên lén lút, nhưng câu hỏi của bạn dường như có thể được trả lời lại là "làm thế nào để tôi nghĩ về những điều tôi không nghĩ đến?". Không chắc chắn những gì phải làm với TDD.
Jared Smith

Câu trả lời:


57

Đây là các loại lỗi bạn thường thấy trong bước tái cấu trúc của red / green / refactor. Đừng quên bước đó! Hãy xem xét một cấu trúc lại như sau (chưa được kiểm tra):

def pluralize(num, unit):
    if num == 1:
        return unit
    else:
        return unit + "s"

def convert_to_unit(delta, unit):
    factor = 1
    if unit == "week":
        factor = 7 
    elif unit == "month":
        factor = 30
    elif unit == "year":
        factor = 365
    return delta // factor

def best_unit(delta):
    if delta < 7:
        return "day"
    elif delta < 30:
        return "week"
    elif delta < 365:
        return "month"
    else:
        return "year"

def human_friendly(event_date):
    date = event_date.date()
    today = now.date()
    yesterday = today - datetime.timedelta(1)
    if date == today:
        return "Today"
    elif date == yesterday:
        return "Yesterday"
    else:
        delta = (now - event_date).days
        unit = best_unit(delta)
        converted = convert_to_unit(delta, unit)
        pluralized = pluralize(converted, unit)
        return "{} {} ago".format(converted, pluralized)

Ở đây bạn đã tạo ra 3 hàm ở mức độ trừu tượng thấp hơn, có tính gắn kết cao hơn và dễ kiểm tra hơn. Nếu bạn bỏ qua một khoảng thời gian bạn dự định, nó sẽ dính ra như ngón tay cái đau trong các chức năng trợ giúp đơn giản hơn. Ngoài ra, bằng cách loại bỏ trùng lặp, bạn giảm nguy cơ lỗi. Bạn thực sự sẽ phải thêm mã để thực hiện trường hợp bị hỏng của bạn.

Các trường hợp kiểm tra tinh tế khác cũng dễ dàng hơn trong tâm trí khi nhìn vào một hình thức tái cấu trúc như thế này. Ví dụ, nên best_unitlàm gì nếu deltaâm tính?

Nói cách khác, tái cấu trúc không chỉ để làm cho nó đẹp. Nó giúp con người dễ dàng phát hiện ra các lỗi mà trình biên dịch không thể.


12
Bước tiếp theo là quốc tế hóa, và pluralizechỉ làm việc cho một tập hợp các từ tiếng Anh sẽ là một trách nhiệm pháp lý.
Ded

@Ded repeatator chắc chắn, nhưng sau đó tùy thuộc vào ngôn ngữ / văn hóa mà bạn nhắm mục tiêu, bạn có thể thoát khỏi việc chỉ sửa đổi pluralizebằng cách sử dụng numunitxây dựng một khóa nào đó để kéo một chuỗi định dạng từ một số tệp bảng / tài nguyên. HOẶC bạn có thể cần viết lại hoàn toàn logic, bởi vì bạn cần các đơn vị khác nhau ;-)
Hulk

4
Một vấn đề còn tồn tại ngay cả với việc tái cấu trúc này, đó là "ngày hôm qua" không có ý nghĩa gì trong vài giờ sáng (ngay sau 12:01 sáng). Theo thuật ngữ thân thiện với con người, điều gì đó đã xảy ra lúc 11:59 tối không đột nhiên thay đổi từ "hôm nay" sang "ngày hôm qua" khi đồng hồ trôi qua nửa đêm. Thay vào đó, nó thay đổi từ "1 phút trước" thành "2 phút trước". "Hôm nay" quá thô thiển về những điều đã xảy ra nhưng vài phút trước và "ngày hôm qua" có nhiều vấn đề với những con cú đêm.
David Hammen

@DavidHammen Đây là một vấn đề về khả năng sử dụng và nó phụ thuộc vào mức độ chính xác mà bạn cần phải có. Khi bạn muốn biết ít nhất là đến giờ, tôi sẽ không nghĩ "ngày hôm qua" là tốt. "24 giờ trước" rõ ràng hơn nhiều và là cách diễn đạt thường được sử dụng của con người để nhấn mạnh số giờ. Các máy tính đang cố gắng trở nên "thân thiện với con người" hầu như luôn hiểu sai điều này và khái quát nó quá mức thành "ngày hôm qua", điều này quá mơ hồ. Nhưng để biết điều này bạn sẽ cần phỏng vấn người dùng để xem họ nghĩ gì. Đối với một số điều bạn thực sự muốn ngày giờ chính xác, vì vậy "ngày hôm qua" luôn sai.
Brandin

149

Phát triển theo hướng kiểm tra đã không giúp đỡ.

Có vẻ như nó đã giúp ích, chỉ là bạn chưa có bài kiểm tra cho kịch bản "một ngày trước". Có lẽ, bạn đã thêm một bài kiểm tra sau khi trường hợp này được tìm thấy; Đây vẫn là TDD, khi phát hiện ra lỗi, bạn viết một bài kiểm tra đơn vị để phát hiện lỗi, sau đó sửa nó.

Nếu bạn quên viết một bài kiểm tra cho một hành vi, TDD không có gì để giúp bạn; bạn quên viết bài kiểm tra và do đó không viết bài thực hiện.


2
Bất cứ điều gì có thể được thực hiện rằng nếu nhà phát triển đã không sử dụng tdd, họ cũng có nhiều khả năng bỏ lỡ các trường hợp khác.
Caleb

75
Và trên hết, hãy nghĩ xem họ đã tiết kiệm được bao nhiêu thời gian khi họ sửa lỗi? Bằng cách thực hiện các thử nghiệm hiện tại, họ biết ngay rằng thay đổi của họ không phá vỡ hành vi hiện có. Và họ được tự do thêm các trường hợp thử nghiệm mới và cấu trúc lại mà không phải chạy các thử nghiệm thủ công mở rộng sau đó.
Caleb

15
TDD chỉ tốt như các bài kiểm tra viết.
Mindwin

Một quan sát khác: thêm thử nghiệm cho trường hợp này sẽ cải thiện thiết kế, bằng cách buộc chúng tôi datetime.utcnow()loại bỏ chức năng đó, và thay vào đó để chuyển qua nowlàm đối số (có thể tái tạo) thay thế.
Toby Speight

114

một sự kiện xảy ra hai mươi sáu giờ trước sẽ là một ngày trước

Các xét nghiệm sẽ không giúp được nhiều nếu một vấn đề được xác định kém. Rõ ràng bạn đang trộn ngày dương lịch với ngày được tính theo giờ. Nếu bạn dính vào các ngày theo lịch, thì vào lúc 1 giờ sáng, 26 giờ trước không phải là ngày hôm qua. Và nếu bạn dính vào giờ, thì 26 giờ trước làm tròn đến 1 ngày trước bất kể thời gian.


45
Đây là một điểm tuyệt vời để thực hiện. Thiếu một yêu cầu không hoàn toàn có nghĩa là quá trình thực hiện của bạn không thành công. Nó chỉ có nghĩa là yêu cầu không được xác định rõ. (Hoặc bạn chỉ đơn giản là đã gây ra lỗi của con người, điều này sẽ xảy ra theo thời gian)
Caleb

Đây là câu trả lời tôi muốn thực hiện. Tôi xác định thông số kỹ thuật là "nếu sự kiện là ngày theo lịch này, hãy trình bày delta theo giờ. Khác chỉ sử dụng ngày để xác định delta" Giờ kiểm tra chỉ hữu ích trong một ngày, nếu vượt quá độ phân giải của bạn có nghĩa là ngày.
Baldrickk

1
Tôi thích câu trả lời này vì nó chỉ ra vấn đề thực sự: điểm trong thời gian và ngày là hai đại lượng khác nhau. Chúng có liên quan nhưng khi bạn bắt đầu so sánh chúng, mọi thứ đi về phía nam rất nhanh. Trong lập trình, logic ngày và thời gian là một trong những điều khó nhất để làm đúng. Tôi thực sự không thích rằng rất nhiều ngày thực hiện về cơ bản lưu trữ ngày là 0:00 điểm. Nó làm cho rất nhiều nhầm lẫn.
Pieter B

38

Bạn không thể. TDD rất tuyệt vời trong việc bảo vệ bạn khỏi những vấn đề có thể bạn biết. Sẽ không có ích gì nếu bạn gặp phải những vấn đề bạn chưa từng xem xét. Đặt cược tốt nhất của bạn là nhờ người khác kiểm tra hệ thống, họ có thể tìm thấy các trường hợp cạnh mà bạn chưa từng xem xét.

Đọc liên quan: Có thể đạt đến trạng thái lỗi không tuyệt đối cho phần mềm quy mô lớn không?


2
Có các bài kiểm tra được viết bởi một người khác không phải nhà phát triển luôn là một ý tưởng hay, điều đó có nghĩa là cả hai bên cần bỏ qua cùng một điều kiện đầu vào cho lỗi để đưa nó vào sản xuất.
Michael Kay

35

Có hai cách tiếp cận tôi thường làm mà tôi thấy có thể giúp ích.

Đầu tiên, tôi tìm kiếm các trường hợp cạnh. Đây là những nơi mà hành vi thay đổi. Trong trường hợp của bạn, hành vi thay đổi tại một số điểm dọc theo chuỗi ngày nguyên dương. Có một trường hợp cạnh ở mức 0, tại một, lúc bảy, v.v. Sau đó tôi sẽ viết các trường hợp kiểm tra tại và xung quanh các trường hợp cạnh. Tôi có các trường hợp thử nghiệm ở -1 ngày, 0 ngày, 1 giờ, 23 giờ, 24 giờ, 25 giờ, 6 ngày, 7 ngày, 8 ngày, v.v.

Điều thứ hai tôi tìm kiếm là mô hình hành vi. Trong logic của bạn trong nhiều tuần, bạn có xử lý đặc biệt trong một tuần. Bạn có thể có logic tương tự trong mỗi khoảng thời gian khác của bạn không được hiển thị. Logic này không có mặt trong nhiều ngày, mặc dù. Tôi sẽ xem xét điều đó với sự nghi ngờ cho đến khi tôi có thể giải thích rõ ràng lý do tại sao trường hợp đó lại khác hoặc tôi thêm logic vào.


9
Đây là một phần thực sự quan trọng của TDD thường bị bỏ qua và tôi hiếm khi thấy nói về các bài báo và hướng dẫn - điều thực sự quan trọng là kiểm tra các trường hợp cạnh và điều kiện biên vì tôi thấy đó là nguồn gốc của 90% lỗi - bởi -một lỗi, trên và dưới, ngày cuối cùng của tháng, tháng cuối cùng của năm, năm nhuận, v.v.
GoatInTheMachine

2
@GoatInTheMachine - và 90% trong số 90% lỗi đó xoay quanh việc chuyển đổi thời gian tiết kiệm ánh sáng ban ngày ..... Hahaha
Caleb

1
Trước tiên, bạn có thể phân chia các đầu vào có thể trong các lớp tương đương và sau đó xác định các trường hợp cạnh ở biên của các lớp. Của chúng tôi đó là một nỗ lực có thể lớn hơn nỗ lực phát triển; việc đó có đáng hay không phụ thuộc vào mức độ quan trọng của việc cung cấp phần mềm không có lỗi nhất có thể, thời hạn là bao nhiêu và bạn có bao nhiêu tiền và sự kiên nhẫn.
Peter - Hồi phục lại

2
Đây là câu trả lời chính xác. Rất nhiều quy tắc busines yêu cầu bạn chia một phạm vi các giá trị thành các khoảng trong đó chúng là các trường hợp được xử lý theo các cách khác nhau.
abuzittin gillifirca

14

Bạn không thể bắt lỗi logic có trong yêu cầu của bạn với TDD. Nhưng vẫn còn, TDD giúp. Rốt cuộc, bạn đã tìm thấy lỗi và thêm một trường hợp thử nghiệm. Nhưng về cơ bản, TDD chỉ đảm bảo rằng mã phù hợp với mô hình tinh thần của bạn. Nếu mô hình tinh thần của bạn bị thiếu sót, các trường hợp thử nghiệm sẽ không bắt được chúng.

Nhưng hãy lưu ý, trong khi sửa lỗi, các trường hợp kiểm tra mà bạn đã đảm bảo không có hành vi chức năng hiện có nào bị hỏng. Điều đó khá quan trọng, thật dễ dàng để sửa một lỗi nhưng giới thiệu một lỗi khác.

Để tìm ra các lỗi đó trước đó, bạn thường cố gắng sử dụng các trường hợp kiểm tra dựa trên lớp tương đương. sử dụng nguyên tắc đó, bạn sẽ chọn một trường hợp từ mọi lớp tương đương, và sau đó là tất cả các trường hợp cạnh.

Bạn sẽ chọn một ngày từ hôm nay, hôm qua, vài ngày trước, chính xác một tuần trước và vài tuần trước làm ví dụ từ mỗi lớp tương đương. Khi kiểm tra ngày, bạn cũng sẽ đảm bảo rằng các kiểm tra của bạn không sử dụng ngày hệ thống, nhưng sử dụng ngày được xác định trước để so sánh. Điều này cũng sẽ làm nổi bật một số trường hợp cạnh: Bạn sẽ đảm bảo chạy thử nghiệm vào một số thời điểm tùy ý trong ngày, bạn sẽ chạy nó trực tiếp sau nửa đêm, trực tiếp trước nửa đêm và thậm chí trực tiếp vào nửa đêm. Điều này có nghĩa là đối với mỗi thử nghiệm, sẽ có bốn lần cơ sở được thử nghiệm.

Sau đó, bạn sẽ thêm các trường hợp cạnh vào tất cả các lớp khác một cách có hệ thống. Bạn có bài kiểm tra cho ngày hôm nay. Vì vậy, thêm một thời gian ngay trước và sau khi hành vi nên chuyển đổi. Ngày hôm qua cũng vậy. Tương tự cho một tuần trước, vv

Rất có thể là bằng cách liệt kê tất cả các trường hợp cạnh một cách có hệ thống và viết ra các trường hợp kiểm tra cho chúng, bạn phát hiện ra rằng đặc điểm kỹ thuật của bạn đang thiếu một số chi tiết và thêm nó. Lưu ý rằng việc xử lý ngày là điều mọi người thường mắc phải, bởi vì mọi người thường quên viết bài kiểm tra của họ để có thể chạy với thời gian khác nhau.

Tuy nhiên, lưu ý rằng hầu hết những gì tôi đã viết ít liên quan đến TDD. Đó là về việc viết ra các lớp tương đương và đảm bảo các thông số kỹ thuật của riêng bạn đủ chi tiết về chúng. Đó là quá trình mà bạn giảm thiểu các lỗi logic. TDD chỉ đảm bảo mã của bạn phù hợp với mô hình tinh thần của bạn.

Đến với các trường hợp thử nghiệm là khó khăn . Thử nghiệm dựa trên lớp tương đương không phải là kết thúc của tất cả, và trong một số trường hợp, nó có thể làm tăng đáng kể số lượng các trường hợp thử nghiệm. Trong thế giới thực, việc thêm tất cả các thử nghiệm đó thường không hiệu quả về mặt kinh tế (mặc dù về lý thuyết, nó nên được thực hiện).


12

Cách duy nhất tôi có thể nghĩ đến là thêm rất nhiều khẳng định cho các trường hợp mà tôi tin rằng sẽ không bao giờ xảy ra (như tôi tin rằng một ngày trước nhất thiết phải là ngày hôm qua), và sau đó lặp lại từng giây trong mười năm qua, kiểm tra bất kỳ vi phạm khẳng định, có vẻ quá phức tạp.

Tại sao không? Điều này nghe có vẻ là một ý tưởng khá tốt!

Thêm hợp đồng (xác nhận) vào mã là một cách khá chắc chắn để cải thiện tính chính xác của nó. Nói chung, chúng tôi thêm chúng làm điều kiện tiên quyết cho mục nhập chức năng và hậu điều kiện khi trả về chức năng. Ví dụ, chúng ta có thể thêm một hậu điều rằng tất cả các giá trị trả lại là một trong hai hình thức "A [đơn vị] trước" hoặc "[số] [đơn vị] s trước". Khi được thực hiện một cách có kỷ luật, điều này dẫn đến việc thiết kế theo hợp đồng và là một trong những cách phổ biến nhất để viết mã có độ đảm bảo cao.

Quan trọng, các hợp đồng không có ý định được thử nghiệm; chúng chỉ là nhiều thông số kỹ thuật của mã của bạn như các bài kiểm tra của bạn. Tuy nhiên, bạn có thể kiểm tra thông qua các hợp đồng: gọi mã trong bài kiểm tra của bạn và, nếu không có hợp đồng nào phát sinh lỗi, bài kiểm tra sẽ vượt qua. Vòng qua từng giây trong mười năm qua là một chút nhiều. Nhưng chúng ta có thể tận dụng một phong cách thử nghiệm khác gọi là thử nghiệm dựa trên đặc tính .

Trong PBT thay vì kiểm tra các đầu ra cụ thể của mã, bạn kiểm tra xem đầu ra có tuân theo một số thuộc tính không. Ví dụ, một thuộc tính của reverse()hàm là cho bất kỳ danh sách nào l, reverse(reverse(l)) = l. Mặt trái của các bài kiểm tra viết như thế này là bạn có thể có công cụ PBT tạo ra vài trăm danh sách tùy ý (và một vài danh sách bệnh lý) và kiểm tra tất cả chúng đều có thuộc tính này. Nếu không , động cơ "thu nhỏ" trường hợp không thành công để tìm danh sách tối thiểu phá vỡ mã của bạn. Có vẻ như bạn đang viết Python, có Giả thuyết là khung PBT chính.

Vì vậy, nếu bạn muốn một cách tốt để tìm ra các trường hợp khó khăn hơn mà bạn có thể không nghĩ tới, sử dụng các hợp đồng và thử nghiệm dựa trên tài sản cùng nhau sẽ giúp ích rất nhiều. Tất nhiên, điều này không thay thế các bài kiểm tra đơn vị bằng văn bản, nhưng nó làm tăng thêm nó, đây thực sự là điều tốt nhất chúng ta có thể làm với tư cách là kỹ sư.


2
Đây chính xác là giải pháp đúng cho loại vấn đề này. Tập hợp các đầu ra hợp lệ rất dễ xác định (bạn có thể đưa ra một biểu thức chính quy rất đơn giản, đại loại như vậy /(today)|(yesterday)|([2-6] days ago)|...) và sau đó bạn có thể chạy quy trình với các đầu vào được chọn ngẫu nhiên cho đến khi bạn tìm thấy một đầu ra không nằm trong tập hợp các đầu ra dự kiến. Thực hiện phương pháp này sẽ bắt được lỗi này và sẽ không yêu cầu nhận ra rằng lỗi có thể tồn tại trước đó.
Jules

@Jules Xem thêm kiểm tra / kiểm tra tài sản . Tôi thường viết các bài kiểm tra tài sản trong quá trình phát triển, để đề cập đến càng nhiều trường hợp không lường trước càng tốt và buộc tôi phải nghĩ đến các tính chất / bất biến chung. Tôi lưu các bài kiểm tra một lần cho hồi quy và như vậy (mà vấn đề của tác giả là một ví dụ)
Warbo

1
Nếu bạn thực hiện nhiều vòng lặp đó trong các bài kiểm tra, nếu mất một thời gian rất dài, sẽ đánh bại một trong những mục tiêu chính của kiểm thử đơn vị: chạy nhanh các bài kiểm tra !
CJ Dennis

5

Đây là một ví dụ trong đó việc thêm một chút mô-đun sẽ có ích. Nếu một đoạn mã dễ bị lỗi được sử dụng nhiều lần, thì tốt nhất bạn nên bọc nó trong một hàm nếu có thể.

def time_ago(delta, unit):
    delta_str = _number_to_text(delta) + " " + unit;
    if delta == 1:
        return delta_str + " ago"
    else:
        return delta_str = "s ago"

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return time_ago(delta, "day")

if delta < 30:
    weeks = math.floor(delta / 7)
    return time_ago(weeks, "week")

if delta < 365:
    months = math.floor(delta / 31)
    return time_ago(months, "month")

5

Phát triển theo hướng kiểm tra đã không giúp đỡ.

TDD hoạt động tốt nhất như một kỹ thuật nếu người viết các bài kiểm tra là bất lợi. Điều này thật khó khăn nếu bạn không lập trình cặp, vì vậy một cách khác để suy nghĩ về điều này là:

  • Đừng viết kiểm tra để xác nhận chức năng được kiểm tra hoạt động như bạn đã thực hiện. Viết các bài kiểm tra cố tình phá vỡ nó.

Đây là một nghệ thuật khác, áp dụng cho việc viết mã chính xác có hoặc không có TDD, và có lẽ phức tạp (nếu không phải như vậy) so với việc viết mã thực sự. Đó là một cái gì đó bạn cần thực hành, và một cái gì đó không có câu trả lời đơn giản, dễ dàng, đơn giản cho.

Kỹ thuật cốt lõi để viết phần mềm mạnh mẽ, cũng là kỹ thuật cốt lõi để hiểu cách viết các bài kiểm tra hiệu quả:

Hiểu các điều kiện tiên quyết cho một hàm - các trạng thái hợp lệ (nghĩa là bạn đang đưa ra giả định gì về trạng thái của lớp mà hàm là một phương thức) và các phạm vi tham số đầu vào hợp lệ - mỗi loại dữ liệu có một phạm vi các giá trị có thể - một tập hợp con trong đó sẽ được xử lý bởi chức năng của bạn.

Nếu bạn không làm gì đơn giản hơn là kiểm tra rõ ràng các giả định này khi nhập chức năng và đảm bảo rằng vi phạm được ghi lại hoặc ném và / hoặc các lỗi chức năng không xử lý thêm, bạn có thể nhanh chóng biết liệu phần mềm của mình có bị lỗi trong sản xuất hay không, làm cho nó mạnh mẽ và chịu lỗi, và phát triển kỹ năng viết bài kiểm tra nghịch cảnh của bạn.


Lưu ý Có cả một tài liệu về Điều kiện trước và sau, bất biến, v.v., cùng với các thư viện có thể áp dụng chúng bằng các thuộc tính. Cá nhân tôi không phải là một người hâm mộ của việc đi quá trang trọng, nhưng nó đáng để xem xét.


1

Đây là một trong những sự thật quan trọng nhất về phát triển phần mềm: Hoàn toàn không thể viết mã không có lỗi.

TDD sẽ không cứu bạn khỏi việc giới thiệu các lỗi tương ứng với các trường hợp thử nghiệm mà bạn không nghĩ tới. Nó cũng sẽ không cứu bạn khỏi việc viết một bài kiểm tra không chính xác mà không nhận ra nó, sau đó viết mã không chính xác xảy ra để vượt qua bài kiểm tra lỗi. Và mọi kỹ thuật phát triển phần mềm đơn lẻ khác từng được tạo ra đều có những lỗ hổng tương tự. Là nhà phát triển, chúng ta là con người không hoàn hảo. Vào cuối ngày, không có cách nào để viết mã không có lỗi 100%. Nó không bao giờ có và sẽ không bao giờ xảy ra.

Điều này không có nghĩa là bạn nên từ bỏ hy vọng. Mặc dù không thể viết mã hoàn hảo, nhưng rất có thể viết mã có quá ít lỗi xuất hiện trong các trường hợp cạnh hiếm gặp như vậy mà phần mềm cực kỳ thực tế để sử dụng. Phần mềm không thể hiện hành vi lỗi trong thực tế là rất có thể viết.

Nhưng viết nó đòi hỏi chúng ta phải nắm bắt thực tế rằng chúng ta sẽ sản xuất phần mềm lỗi. Hầu hết mọi thực hành phát triển phần mềm hiện đại đều ở một mức độ nào đó được xây dựng xung quanh hoặc ngăn chặn lỗi xuất hiện ngay từ đầu hoặc bảo vệ bản thân khỏi hậu quả của các lỗi mà chúng tôi chắc chắn tạo ra:

  • Thu thập các yêu cầu kỹ lưỡng cho phép chúng tôi biết hành vi không chính xác trông như thế nào trong mã của chúng tôi.
  • Viết mã sạch, được kiến ​​trúc cẩn thận giúp dễ dàng tránh giới thiệu lỗi ngay từ đầu và dễ sửa chúng hơn khi chúng tôi xác định chúng.
  • Kiểm tra viết cho phép chúng tôi tạo ra một bản ghi về những gì chúng tôi tin rằng nhiều lỗi tồi tệ nhất có thể có trong phần mềm của chúng tôi sẽ là và chứng minh rằng chúng tôi tránh được ít nhất những lỗi đó. TDD tạo ra các thử nghiệm đó trước mã, BDD lấy các thử nghiệm đó từ các yêu cầu và thử nghiệm đơn vị lỗi thời tạo ra các thử nghiệm sau khi mã được viết, nhưng tất cả đều ngăn chặn các hồi quy tồi tệ nhất trong tương lai.
  • Đánh giá ngang hàng có nghĩa là mỗi lần thay đổi mã, ít nhất hai cặp mắt đã nhìn thấy mã, giảm mức độ thường xuyên xảy ra lỗi.
  • Sử dụng trình theo dõi lỗi hoặc trình theo dõi câu chuyện người dùng xử lý lỗi như câu chuyện của người dùng có nghĩa là khi lỗi xuất hiện, họ sẽ theo dõi và xử lý cuối cùng, không bị lãng quên và bỏ mặc để theo cách của người dùng.
  • Sử dụng một máy chủ dàn có nghĩa là trước khi phát hành chính, bất kỳ lỗi trình chiếu nào cũng có cơ hội xuất hiện và được xử lý.
  • Sử dụng kiểm soát phiên bản có nghĩa là trong trường hợp xấu nhất, khi mã có lỗi lớn được chuyển đến khách hàng, bạn có thể thực hiện khôi phục khẩn cấp và nhận lại sản phẩm đáng tin cậy trong tay khách hàng trong khi bạn sắp xếp mọi thứ.

Giải pháp tối ưu cho vấn đề bạn đã xác định không phải là chống lại thực tế là bạn không thể đảm bảo bạn sẽ viết mã không có lỗi, mà là nắm lấy nó. Nắm bắt các thực tiễn tốt nhất trong ngành trong tất cả các lĩnh vực của quy trình phát triển của bạn và bạn sẽ liên tục cung cấp mã cho người dùng của mình rằng, mặc dù không hoàn hảo, nhưng vẫn đủ mạnh mẽ cho công việc.


1

Bạn chỉ đơn giản là không nghĩ về trường hợp này trước đây và do đó không có trường hợp thử nghiệm cho nó.

Điều này xảy ra tất cả các thời gian và chỉ là bình thường. Đó luôn là sự đánh đổi bao nhiêu nỗ lực của bạn trong việc tạo ra tất cả các trường hợp thử nghiệm có thể. Bạn có thể dành thời gian vô hạn để xem xét tất cả các trường hợp thử nghiệm.

Đối với máy bay tự động, bạn sẽ mất nhiều thời gian hơn so với một công cụ đơn giản.

Nó thường giúp suy nghĩ về phạm vi hợp lệ của các biến đầu vào của bạn và kiểm tra các ranh giới này.

Ngoài ra, nếu người thử nghiệm là một người khác với nhà phát triển, thường sẽ tìm thấy nhiều trường hợp quan trọng hơn.


1

(và tin rằng nó phải liên quan đến múi giờ, mặc dù việc sử dụng UTC thống nhất trong mã)

Đó là một lỗi logic khác trong mã của bạn mà bạn chưa có bài kiểm tra đơn vị :) - phương pháp của bạn sẽ trả về kết quả không chính xác cho người dùng trong các múi giờ không thuộc UTC. Bạn cần chuyển đổi cả "ngay bây giờ" và ngày của sự kiện sang múi giờ địa phương của người dùng trước khi tính toán.

Ví dụ: Ở Úc, một sự kiện xảy ra lúc 9 giờ sáng giờ địa phương. Vào lúc 11 giờ sáng, nó sẽ được hiển thị là "ngày hôm qua" vì ngày UTC đã thay đổi.


0
  • Hãy để người khác viết các bài kiểm tra. Bằng cách này, ai đó không quen với việc triển khai của bạn có thể kiểm tra các tình huống hiếm gặp mà bạn chưa từng nghĩ tới.

  • Nếu có thể, tiêm các trường hợp thử nghiệm như bộ sưu tập. Điều này làm cho việc thêm một bài kiểm tra dễ dàng như thêm một dòng khác như thế nào yield return new TestCase(...). Điều này có thể đi theo hướng thử nghiệm thăm dò , tự động hóa việc tạo ra các trường hợp thử nghiệm: "Hãy xem mã nào trả về tất cả các giây của một tuần trước".


0

Bạn dường như có quan niệm sai lầm rằng nếu tất cả các bài kiểm tra của bạn vượt qua, bạn không có lỗi. Trong thực tế, nếu tất cả các bài kiểm tra của bạn vượt qua, tất cả các hành vi đã biết là chính xác. Bạn vẫn không biết liệu hành vi chưa biết có đúng hay không.

Hy vọng rằng, bạn đang sử dụng bảo hiểm mã với TDD của bạn. Thêm một thử nghiệm mới cho các hành vi bất ngờ. Sau đó, bạn có thể chạy thử nghiệm cho hành vi không mong muốn để xem con đường thực sự đi qua mã. Khi bạn biết hành vi hiện tại, bạn có thể thực hiện thay đổi để sửa lỗi và khi tất cả các bài kiểm tra lại trôi qua, bạn sẽ biết mình đã thực hiện đúng.

Điều này vẫn không có nghĩa là mã của bạn không có lỗi, chỉ là nó tốt hơn trước và một lần nữa tất cả các hành vi đã biết là chính xác!

Sử dụng TDD chính xác không có nghĩa là bạn sẽ viết mã không có lỗi, điều đó có nghĩa là bạn sẽ viết ít lỗi hơn. Bạn nói:

Các yêu cầu tương đối rõ ràng

Điều này có nghĩa là hành vi nhiều hơn một ngày nhưng không phải ngày hôm qua đã được chỉ định trong các yêu cầu? Nếu bạn bỏ lỡ một yêu cầu bằng văn bản, đó là lỗi của bạn. Nếu bạn nhận ra các yêu cầu không đầy đủ như bạn đã mã hóa nó, tốt cho bạn! Nếu mọi người làm việc theo yêu cầu đã bỏ lỡ trường hợp đó, bạn sẽ không tệ hơn những người khác. Mọi người đều phạm sai lầm, và họ càng tinh tế, họ càng dễ bỏ lỡ. Vấn đề lớn ở đây là TDD không ngăn chặn được tất cả các lỗi!


0

Rất dễ phạm phải một lỗi logic ngay cả trong một mã nguồn đơn giản như vậy.

Đúng. Phát triển theo hướng thử nghiệm không thay đổi điều đó. Bạn vẫn có thể tạo lỗi trong mã thực tế và cả mã kiểm tra.

Phát triển theo hướng kiểm tra đã không giúp đỡ.

Ồ, nhưng nó đã làm! Trước hết, khi bạn nhận thấy lỗi bạn đã có khung kiểm tra hoàn chỉnh, và phải sửa lỗi trong kiểm tra (và mã thực tế). Thứ hai, bạn không biết bạn sẽ có thêm bao nhiêu lỗi nếu bạn không thực hiện TDD ngay từ đầu.

Điều đáng lo ngại là tôi không thể thấy làm thế nào có thể tránh được những lỗi như vậy.

Bạn không thể. Thậm chí NASA còn không tìm ra cách để tránh lỗi; con người chúng ta chắc chắn cũng không.

Bên cạnh suy nghĩ nhiều hơn trước khi viết mã,

Đó là một ngụy biện. Một trong những lợi ích lớn nhất của TDD là bạn có thể viết mã với ít suy nghĩ hơn , bởi vì tất cả những bài kiểm tra đó ít nhất là bắt được hồi quy khá tốt. Ngoài ra, ngay cả, hoặc đặc biệt là với TDD, dự kiến ​​sẽ không cung cấp mã không có lỗi ngay từ đầu (hoặc tốc độ phát triển của bạn sẽ bị dừng lại).

cách duy nhất tôi có thể nghĩ đến là thêm nhiều khẳng định cho các trường hợp mà tôi tin rằng sẽ không bao giờ xảy ra (như tôi tin rằng một ngày trước nhất thiết phải là ngày hôm qua), và sau đó lặp đi lặp lại trong mỗi giây trong mười năm qua, kiểm tra bất kỳ vi phạm khẳng định, có vẻ quá phức tạp.

Điều này rõ ràng sẽ mâu thuẫn với nguyên lý chỉ mã hóa những gì bạn thực sự cần ngay bây giờ. Bạn nghĩ rằng bạn cần những trường hợp đó, và nó đã được như vậy. Đó là một đoạn mã không quan trọng; như bạn đã nói không có thiệt hại nào ngoại trừ bạn tự hỏi về nó trong 30 phút.

Đối với mã quan trọng, bạn thực sự có thể làm những gì bạn nói, nhưng không phải cho mã tiêu chuẩn hàng ngày của bạn.

Làm thế nào tôi có thể tránh tạo ra lỗi này ở nơi đầu tiên?

Bạn không. Bạn tin tưởng vào các bài kiểm tra của mình để tìm ra hầu hết các hồi quy; bạn tuân thủ chu trình tái cấu trúc đỏ-xanh, viết thử trước / trong khi mã hóa thực tế và (quan trọng!) bạn thực hiện số tiền tối thiểu cần thiết để thực hiện chuyển đổi đỏ-xanh (không nhiều hơn, không ít hơn). Điều này sẽ kết thúc với một phạm vi kiểm tra tuyệt vời, ít nhất là một kết quả tích cực.

Khi, không, nếu bạn tìm thấy một lỗi, bạn viết một bài kiểm tra để tái tạo lỗi đó và sửa lỗi với số lượng công việc ít nhất để thực hiện kiểm tra nói từ màu đỏ sang màu xanh lá cây.


-2

Bạn vừa phát hiện ra rằng dù bạn có cố gắng thế nào, bạn sẽ không bao giờ có thể bắt được tất cả các lỗi có thể có trong mã của mình.

Vì vậy, điều này có nghĩa là ngay cả khi cố gắng bắt tất cả các lỗi là một bài tập vô ích, và vì vậy bạn chỉ nên sử dụng các kỹ thuật như TDD như một cách viết mã tốt hơn, mã có ít lỗi hơn, không phải 0 lỗi.

Điều đó có nghĩa là bạn nên dành ít thời gian hơn để sử dụng các kỹ thuật này và dành thời gian tiết kiệm đó để làm việc theo những cách khác để tìm ra các lỗi lọt qua mạng phát triển.

các lựa chọn thay thế như kiểm thử tích hợp hoặc nhóm thử nghiệm, kiểm tra hệ thống, ghi nhật ký và phân tích các nhật ký đó.

Nếu bạn không thể bắt được tất cả các lỗi, thì bạn phải có một chiến lược để giảm thiểu tác động của các lỗi xảy ra trong quá khứ. Nếu bạn phải làm điều này bằng mọi cách, thì việc nỗ lực nhiều hơn vào việc này có ý nghĩa hơn là cố gắng (vô ích) để ngăn chặn chúng ngay từ đầu.

Rốt cuộc, việc bỏ ra rất nhiều thời gian để viết bài kiểm tra và ngày đầu tiên bạn đưa sản phẩm của mình cho khách hàng, điều đó đặc biệt nếu bạn không biết cách tìm và giải quyết lỗi đó. Giải quyết lỗi sau khi chết và sau khi giao hàng rất quan trọng và cần được chú ý nhiều hơn hầu hết mọi người dành cho việc viết bài kiểm tra đơn vị. Lưu kiểm tra đơn vị cho các bit phức tạp và đừng cố gắng hoàn thiện trước.


Điều này là vô cùng thất bại. That in turn means you should spend less time using these techniques- nhưng bạn vừa nói rằng nó sẽ giúp với ít lỗi hơn?!
JMᴇᴇ 17/07/18

@ JMᴇᴇ thêm một thái độ thực dụng trong đó kỹ thuật khiến bạn cảm thấy phấn khích nhất. Tôi biết những người tự hào rằng họ dành 10 lần viết bài kiểm tra so với mã của họ và họ vẫn có lỗi Vì vậy, rất nhạy cảm, thay vì giáo điều, về kỹ thuật kiểm tra là điều cần thiết. Và các thử nghiệm tích hợp phải được sử dụng bằng mọi cách, vì vậy hãy nỗ lực nhiều hơn vào chúng so với thử nghiệm đơn vị.
gbjbaanb
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.