Làm thế nào để kiểm tra đơn vị làm việc?


23

Tôi đang cố gắng để làm cho mã của mình mạnh mẽ hơn và tôi đã đọc về kiểm thử đơn vị, nhưng tôi thấy rất khó để tìm thấy một sử dụng hữu ích thực sự. Chẳng hạn, ví dụ Wikipedia :

public class TestAdder {
    public void testSum() {
        Adder adder = new AdderImpl();
        assert(adder.add(1, 1) == 2);
        assert(adder.add(1, 2) == 3);
        assert(adder.add(2, 2) == 4);
        assert(adder.add(0, 0) == 0);
        assert(adder.add(-1, -2) == -3);
        assert(adder.add(-1, 1) == 0);
        assert(adder.add(1234, 988) == 2222);
    }
}

Tôi cảm thấy rằng bài kiểm tra này hoàn toàn vô dụng, bởi vì bạn được yêu cầu tự tính toán kết quả mong muốn và kiểm tra nó, tôi cảm thấy như một bài kiểm tra đơn vị tốt hơn ở đây sẽ là

assert(adder.add(a, b) == (a+b));

nhưng sau đó đây chỉ là mã hóa chính chức năng trong thử nghiệm. Ai đó có thể cung cấp cho tôi một ví dụ trong đó thử nghiệm đơn vị thực sự hữu ích? FYI Tôi hiện đang mã hóa hầu hết các hàm "thủ tục" có ~ 10 booleans và một vài int và cho tôi một kết quả int dựa trên điều này, tôi cảm thấy như thử nghiệm đơn vị duy nhất tôi có thể làm là đơn giản mã hóa lại thuật toán trong kiểm tra. chỉnh sửa: Tôi cũng nên có trước điều này trong khi chuyển mã ruby ​​(có thể được thiết kế xấu) (mà tôi đã không thực hiện)


14
How does unit testing work?Không ai thực sự biết :)
yannis

30
"Bạn được yêu cầu tự tính toán kết quả mong muốn". Làm thế nào mà "hoàn toàn vô dụng"? Làm thế nào khác bạn có thể chắc chắn câu trả lời là đúng?
S.Lott

9
@ S.Lott: Được gọi là sự tiến bộ, thời xưa người ta dùng máy tính để bẻ số và tiết kiệm thời gian, thời hiện đại người ta dành thời gian để đảm bảo máy tính có thể bẻ số: D
Coder

2
@Coder: mục đích của kiểm tra đơn vị không phải là "để crunch số và tiết kiệm thời gian";)
Andres F.

7
@lezebulon: ví dụ từ Wikipedia không tốt lắm, nhưng đó là một vấn đề với trường hợp thử nghiệm cụ thể đó, không phải với thử nghiệm đơn vị nói chung. Khoảng một nửa dữ liệu thử nghiệm từ ví dụ không thêm bất cứ điều gì mới, khiến nó trở nên dư thừa (tôi sợ phải nghĩ tác giả của bài kiểm tra đó sẽ làm gì với các tình huống phức tạp hơn). Một thử nghiệm có ý nghĩa hơn sẽ phân vùng dữ liệu thử nghiệm trong ít nhất các trường hợp sau: "nó có thể thêm số âm không?", "Có trung tính không?", "Nó có thể thêm số âm và số dương không?".
Andres F.

Câu trả lời:


26

Các bài kiểm tra đơn vị, nếu bạn đang kiểm tra các đơn vị đủ nhỏ, luôn khẳng định sự rõ ràng rõ ràng.

Lý do add(x, y)thậm chí được đề cập đến một bài kiểm tra đơn vị, là bởi vì đôi khi sau đó ai đó sẽ đi vào addvà đưa mã xử lý logic thuế đặc biệt không nhận ra rằng add được sử dụng ở mọi nơi.

Các bài kiểm tra đơn vị rất nhiều về nguyên tắc kết hợp: nếu A làm B và B làm C, thì A làm C. "A làm C" là một bài kiểm tra cấp cao hơn. Ví dụ, hãy xem xét các mã doanh nghiệp hoàn toàn hợp pháp sau đây:

public void LoginUser (string username, string password) {
    var user = db.FetchUser (username);

    if (user.Password != password)
        throw new Exception ("invalid password");

    var roles = db.FetchRoles (user);

    if (! roles.Contains ("member"))
        throw new Exception ("not a member");

    Session["user"] = user;
}

Thoạt nhìn, đây có vẻ là một phương pháp tuyệt vời để kiểm tra đơn vị, bởi vì nó có mục đích rất rõ ràng. Tuy nhiên, nó làm khoảng 5 điều khác nhau. Mỗi điều có một trường hợp hợp lệ và không hợp lệ, và sẽ tạo ra một hoán vị rất lớn của các bài kiểm tra đơn vị. Lý tưởng nhất là chia nhỏ hơn nữa:

public void LoginUser (string username, string password) {

    var user = _userRepo.FetchValidUser (username, password);

    _rolesRepo.CheckUserForRole (user, "member");

    _localStorage.StoreValue ("user", user);
}

Bây giờ chúng tôi xuống đơn vị. Một bài kiểm tra đơn vị không quan tâm những gì _userRepoxem xét hành vi hợp lệ FetchValidUser, chỉ có điều nó được gọi. Bạn có thể sử dụng một thử nghiệm khác để đảm bảo chính xác những gì người dùng hợp lệ cấu thành. Tương tự như vậy đối với CheckUserForRole... bạn đã tách bài kiểm tra của mình để biết cấu trúc Vai trò trông như thế nào. Bạn cũng đã tách toàn bộ chương trình của mình khỏi bị ràng buộc chặt chẽ Session. Tôi tưởng tượng tất cả các mảnh còn thiếu ở đây sẽ trông như:

class UserRepository : IUserRepository
{
    public User FetchValidUser (string username, string password)
    {
        var user = db.FetchUser (username);

        if (user.Password != password)
            throw new Exception ("invalid password");

        return user;
    }
}

class RoleRepository : IRoleRepository
{
    public void CheckUserForRole (User user, string role)
    {
        var roles = db.FetchRoles (user);

        if (! roles.Contains (role))
            throw new Exception ("not a member");
    }
}

class SessionStorage : ILocalStorage
{
    public void StoreValue (string key, object value)
    {
        Session[key] = value;
    }
}

Bằng cách tái cấu trúc, bạn đã hoàn thành một số việc cùng một lúc. Chương trình hỗ trợ nhiều hơn cho việc xé các cấu trúc cơ bản (bạn có thể bỏ lớp cơ sở dữ liệu cho NoQuery) hoặc thêm khóa một cách liền mạch khi bạn nhận ra Sessionkhông an toàn cho luồng hoặc bất cứ điều gì. Bây giờ bạn cũng đã cho mình những bài kiểm tra rất đơn giản để viết cho ba phụ thuộc này.

Hi vọng điêu nay co ich :)


13

Tôi hiện đang mã hóa hầu hết các hàm "thủ tục" mất ~ 10 booleans và một vài int và cho tôi một kết quả int dựa trên điều này, tôi cảm thấy như thử nghiệm đơn vị duy nhất tôi có thể làm chỉ đơn giản là mã hóa lại thuật toán trong thử nghiệm

Tôi khá chắc chắn rằng mỗi một trong các hàm thủ tục của bạn là xác định, do đó, nó trả về một kết quả int cụ thể cho mỗi bộ giá trị đầu vào đã cho. Lý tưởng nhất, bạn sẽ có một đặc tả chức năng mà từ đó bạn có thể tìm ra kết quả nào bạn sẽ nhận được cho các bộ giá trị đầu vào nhất định. Thiếu điều đó, bạn có thể chạy mã ruby ​​(được cho là hoạt động chính xác) cho các bộ giá trị đầu vào nhất định và ghi lại kết quả. Sau đó, bạn cần CỨNG MÃ kết quả vào bài kiểm tra của mình. Thử nghiệm được coi là một bằng chứng cho thấy mã của bạn thực sự tạo ra kết quả được biết là chính xác .


+1 để chạy mã hiện có và ghi lại kết quả. Trong tình huống này, đó có lẽ là cách tiếp cận thực dụng.
MarkJ

12

Vì dường như không ai khác đã cung cấp một ví dụ thực tế:

    public void testRoman() {
        RomanNumeral numeral = new RomanNumeral();
        assert( numeral.toRoman(1) == "I" )
        assert( numeral.toRoman(4) == "IV" )
        assert( numeral.toRoman(5) == "V" )
        assert( numeral.toRoman(9) == "IX" )
        assert( numeral.toRoman(10) == "X" )
    }
    public void testSqrt() {
        assert( sqrt(4) == 2 )
        assert( sqrt(9) == 3 )
    }

Bạn nói:

Tôi cảm thấy rằng bài kiểm tra này hoàn toàn vô dụng, bởi vì bạn được yêu cầu tự tính toán kết quả mong muốn và kiểm tra nó

Nhưng vấn đề là bạn ít có khả năng mắc lỗi (hoặc ít nhất có nhiều khả năng nhận thấy lỗi của mình) khi thực hiện các tính toán thủ công sau đó khi mã hóa.

Bạn có khả năng mắc lỗi trong mã chuyển đổi thập phân sang chữ thập phân của bạn như thế nào? Rất có thể. Bạn có khả năng mắc lỗi như thế nào khi chuyển đổi số thập phân sang chữ số La Mã bằng tay? Không có khả năng lắm. Đó là lý do tại sao chúng tôi kiểm tra chống lại các tính toán thủ công.

Bạn có khả năng mắc lỗi như thế nào khi thực hiện hàm căn bậc hai? Rất có thể. Bạn có thể mắc lỗi như thế nào khi tính căn bậc hai bằng tay? Có lẽ nhiều khả năng. Nhưng với sqrt, bạn có thể sử dụng máy tính để có câu trả lời.

FYI Tôi hiện đang mã hóa hầu hết các hàm "thủ tục" có ~ 10 booleans và một vài int và cho tôi một kết quả int dựa trên điều này, tôi cảm thấy như thử nghiệm đơn vị duy nhất tôi có thể làm chỉ đơn giản là mã hóa lại thuật toán trong kiểm tra

Vì vậy, tôi sẽ suy đoán về những gì đang xảy ra ở đây. Các chức năng của bạn khá phức tạp, vì vậy thật khó để tìm ra từ đầu vào những gì đầu ra nên là. Để làm điều đó, bạn phải thực hiện thủ công (trong đầu) chức năng để tìm ra đầu ra là gì. Có thể hiểu, điều đó có vẻ vô dụng và dễ bị lỗi.

Điều quan trọng là bạn muốn tìm đầu ra chính xác. Nhưng bạn phải kiểm tra những kết quả đầu ra dựa trên cái gì đó được biết là chính xác. Thật không tốt khi viết thuật toán của riêng bạn để tính toán điều đó bởi vì điều đó rất có thể không chính xác. Trong trường hợp này quá khó để tự tính toán các giá trị.

Tôi sẽ quay trở lại mã ruby ​​và thực thi các hàm ban đầu này với các tham số khác nhau. Tôi sẽ lấy kết quả của mã ruby ​​và đưa chúng vào bài kiểm tra đơn vị. Bằng cách đó, bạn không phải thực hiện tính toán thủ công. Nhưng bạn đang thử nghiệm so với mã gốc. Điều đó sẽ giúp giữ kết quả như cũ, nhưng nếu có lỗi trong bản gốc thì nó sẽ không giúp bạn. Về cơ bản, bạn có thể coi mã gốc giống như máy tính trong ví dụ sqrt.

Nếu bạn đã hiển thị mã thực tế bạn đang chuyển, chúng tôi có thể cung cấp phản hồi chi tiết hơn về cách tiếp cận vấn đề.


Và nếu mã Ruby có một lỗi mà bạn không biết về mã không có trong mã mới của mình và mã của bạn không thực hiện kiểm tra đơn vị dựa trên các kết quả đầu ra của Ruby, thì cuộc điều tra về lý do tại sao nó thất bại cuối cùng sẽ minh chứng cho bạn và dẫn đến Lỗi Ruby tiềm ẩn được tìm thấy. Điều đó thật tuyệt.
Adam Wuerl

11

Tôi cảm thấy như thử nghiệm đơn vị duy nhất tôi có thể làm sẽ chỉ đơn giản là mã lại thuật toán trong thử nghiệm

Bạn gần như đúng cho một lớp học đơn giản như vậy.

Hãy thử nó cho một máy tính phức tạp hơn .. Giống như một máy tính điểm bowling.

Giá trị của các bài kiểm tra đơn vị dễ thấy hơn khi bạn có các quy tắc "kinh doanh" phức tạp hơn với các kịch bản khác nhau để kiểm tra.

Tôi không nói rằng bạn không nên kiểm tra hoạt động của máy tính mill (Tài khoản máy tính của bạn có phát sinh các giá trị như 1/3 không thể biểu diễn không? Nó làm gì với phép chia cho số 0?) Nhưng bạn sẽ thấy giá trị rõ ràng hơn nếu bạn kiểm tra một cái gì đó với nhiều chi nhánh hơn để có được bảo hiểm.


4
+1 để lưu ý nó trở nên hữu ích hơn cho các chức năng phức tạp. Điều gì xảy ra nếu bạn quyết định mở rộng adder.add () thành các giá trị dấu phẩy động? Ma trận? Giá trị tài khoản hợp pháp?
joshin4colours

6

Mặc dù nhiệt tình đáng tin cậy về phạm vi bảo hiểm 100% mã, tôi sẽ nói rằng không phải mọi phương pháp nên được kiểm tra đơn vị. Chỉ có chức năng chứa logic kinh doanh có ý nghĩa. Một chức năng chỉ cần thêm số là vô nghĩa để kiểm tra.

Tôi hiện đang mã hóa hầu hết các hàm "thủ tục" mất ~ 10 booleans và một vài int và cho tôi một kết quả int dựa trên điều này

Có vấn đề thực sự của bạn ngay tại đó. Nếu kiểm tra đơn vị có vẻ khó khăn hoặc vô nghĩa thì có khả năng là do lỗi thiết kế. Nếu nó hướng đối tượng nhiều hơn, chữ ký phương thức của bạn sẽ không quá lớn và sẽ có ít đầu vào có thể kiểm tra hơn.

Tôi không cần phải tham gia vào OO của mình vượt trội so với lập trình thủ tục ...


trong trường hợp này, "chữ ký" của phương thức không lớn, tôi chỉ đọc từ một std :: vector <bool> là một thành viên của lớp. Tôi cũng nên biết rằng tôi đang chuyển mã ruby ​​(có thể được thiết kế xấu) (mà tôi đã không thực hiện)
lezebulon

2
@lezebulon Bất kể nếu có nhiều đầu vào khả dĩ cho phương thức đơn lẻ đó chấp nhận thì phương thức đó đang làm quá nhiều .
maple_shaft

3

Theo quan điểm của tôi, các bài kiểm tra đơn vị thậm chí còn hữu ích ở lớp trình cộng nhỏ của bạn: đừng nghĩ đến việc "giải mã" thuật toán và nghĩ về nó như một hộp đen với kiến ​​thức duy nhất bạn có là hành vi chức năng (nếu bạn quen thuộc với phép nhân nhanh, bạn biết một số nỗ lực nhanh hơn, nhưng phức tạp hơn so với sử dụng "a * b") và giao diện chung của bạn. Hơn bạn nên tự hỏi mình "Cái quái gì có thể đi sai?" ...

Trong hầu hết các trường hợp, điều đó xảy ra ở viền (tôi thấy bạn đã kiểm tra thêm các mẫu này ++, -, + -, 00 - thời gian để hoàn thành các mẫu này bằng cách - +, 0+, 0-, +0, -0). Hãy nghĩ về những gì xảy ra ở MAX_INT và MIN_INT khi thêm hoặc bớt (thêm phủ định;)) ở đó. Hoặc cố gắng đảm bảo các bài kiểm tra của bạn trông khá chính xác những gì xảy ra ở khoảng và không.

Tất cả trong tất cả bí mật là rất đơn giản (có thể cho cả những người phức tạp hơn;)) cho các lớp đơn giản: suy nghĩ về các hợp đồng của lớp của bạn (xem thiết kế bằng hợp đồng) và sau đó kiểm tra chúng. Bạn càng biết nhiều hơn về lời mời, trước và sau của bài "hoàn thành" bài kiểm tra của bạn.

Gợi ý cho các lớp kiểm tra của bạn: cố gắng chỉ viết một khẳng định trong một phương thức. Đặt tên cho các phương thức (ví dụ: "testAddingToMaxInt", "testAddingTwoNegative") để có phản hồi tốt nhất khi thử nghiệm của bạn thất bại sau khi thay đổi mã.


2

Thay vì kiểm tra giá trị trả về được tính toán thủ công hoặc sao chép logic trong thử nghiệm để tính giá trị trả về dự kiến, hãy kiểm tra giá trị trả về cho một thuộc tính dự kiến.

Ví dụ: nếu bạn muốn kiểm tra một phương thức đảo ngược ma trận, bạn không muốn đảo ngược thủ công giá trị đầu vào của mình, bạn nên nhân giá trị trả về với đầu vào và kiểm tra xem bạn có nhận được ma trận nhận dạng không.

Để áp dụng phương pháp này cho phương pháp của bạn, bạn sẽ phải xem xét mục đích và ngữ nghĩa của nó, để xác định các thuộc tính nào mà giá trị trả về sẽ có liên quan đến các đầu vào.


2

Kiểm tra đơn vị là một công cụ năng suất. Bạn nhận được một yêu cầu thay đổi, thực hiện nó, sau đó chạy mã của bạn thông qua gambit thử nghiệm đơn vị. Kiểm tra tự động này tiết kiệm thời gian.

I feel that this test is totally useless, because you are required to manually compute the wanted result and test it, I feel like a better unit test here would be

Một điểm moot. Bài kiểm tra trong ví dụ chỉ cho thấy cách khởi tạo một lớp và chạy nó thông qua một loạt các bài kiểm tra. Tập trung vào các chi tiết vụn vặt của một triển khai duy nhất là thiếu rừng cho cây.

Can someone provide me with an example where unit testing is actually useful?

Bạn có một thực thể nhân viên. Các thực thể chứa một tên và địa chỉ. Máy khách quyết định thêm trường báo cáo.

void TestBusinessLayer()
{
   int employeeID = 1234
   Employee employee = Employee.GetEmployee(employeeID)
   BusinessLayer bl = new BusinessLayer()
   Assert.isTrue(bl.Add(employee))//assume Add returns true on pass
}

Đó là một thử nghiệm cơ bản của BL để làm việc với một nhân viên. Mã sẽ vượt qua / thất bại thay đổi lược đồ bạn vừa thực hiện. Hãy nhớ rằng các xác nhận không phải là điều duy nhất mà bài kiểm tra thực hiện. Chạy qua mã cũng đảm bảo không có ngoại lệ được nổi lên.

Theo thời gian, có các bài kiểm tra tại chỗ giúp dễ dàng thực hiện các thay đổi nói chung. Mã được tự động kiểm tra các trường hợp ngoại lệ và chống lại các Xác nhận bạn thực hiện. Điều này tránh được phần lớn chi phí phát sinh khi kiểm tra thủ công bởi một nhóm QA. Mặc dù UI vẫn còn khá khó để tự động hóa, nhưng các lớp khác thường rất dễ giả sử bạn sử dụng các công cụ sửa đổi truy cập một cách chính xác.

I feel like the only unit testing I could do would be to simply re-code the algorithm in the test.

Ngay cả logic thủ tục cũng dễ dàng được gói gọn trong một hàm. Đóng gói, khởi tạo và vượt qua trong int / primitive để được kiểm tra (hoặc đối tượng giả). Không sao chép dán mã vào Kiểm tra đơn vị. Điều đó đánh bại DRY. Nó cũng đánh bại bài kiểm tra hoàn toàn vì bạn không kiểm tra mã, mà là bản sao của mã. Nếu mã cần được thử nghiệm thay đổi, thử nghiệm vẫn vượt qua!


<pedantry> "gamut", không phải "gambit". </ pedantry>
cHao

@chao lol học cái gì đó mới mỗi ngày.
P.Brian.Mackey

2

Lấy ví dụ của bạn (với một chút tái cấu trúc),

assert(a + b, math.add(a, b));

không giúp:

  • hiểu cách math.addcư xử trong nội bộ,
  • biết những gì sẽ xảy ra với các trường hợp cạnh.

Đó là khá nhiều như nói:

  • Nếu bạn muốn biết phương thức này làm gì, hãy tự mình đi xem hàng trăm dòng mã nguồn (bởi vì, vâng, math.add có thể chứa hàng trăm LỘC; xem bên dưới).
  • Tôi không bận tâm để biết liệu phương pháp này hoạt động chính xác. Sẽ ổn nếu cả giá trị mong đợi và giá trị thực tế khác với những gì tôi thực sự mong đợi .

Điều này cũng có nghĩa là bạn không phải thêm các bài kiểm tra như:

assert(3, math.add(1, 2));
assert(4, math.add(2, 2));

Họ không giúp đỡ, hoặc ít nhất, một khi bạn đưa ra khẳng định đầu tiên, điều thứ hai không mang lại điều gì hữu ích.

Thay vào đó, những gì về:

const numeric Pi = 3.1415926535897932384626433832795;
const numeric Expected = 4.1415926535897932384626433832795;
assert(Expected, math.add(Pi, 1),
    "Adding an integer to a long numeric doesn't give a long numeric result.");
assert(Expected, math.add(1, Pi),
    "Adding a long numeric to an integer doesn't give a long numeric result.");

Điều này là tự giải thích và rất hữu ích cho cả bạn và cho người sẽ duy trì mã nguồn sau này. Hãy tưởng tượng rằng người này thực hiện một sửa đổi nhỏ math.addđể đơn giản hóa mã và tối ưu hóa hiệu suất và xem kết quả thử nghiệm như sau:

Test TestNumeric() failed on assertion 2, line 5: Adding a long numeric to an
integer doesn't give a long numeric result.

Expected value: 4.1415926535897932384626433832795
Actual value: 4

người này sẽ hiểu ngay rằng phương thức mới được sửa đổi phụ thuộc vào thứ tự của các đối số: nếu đối số thứ nhất là một số nguyên và đối số thứ hai là một số dài, kết quả sẽ là một số nguyên, trong khi một số dài được mong đợi.

Theo cách tương tự, việc đạt được giá trị thực tế 4.141592ở lần xác nhận đầu tiên là tự giải thích: bạn biết rằng phương pháp này được dự kiến ​​sẽ xử lý với độ chính xác lớn , nhưng thực tế, nó đã thất bại.

Vì lý do rất giống nhau, hai xác nhận sau đây có thể có ý nghĩa trong một số ngôn ngữ:

// We don't expect a concatenation. `math` library is not intended for this.
assert(0, math.add("Hello", "World"));

// We expect the method to convert every string as if it was a decimal.
assert(5, math.add("0x2F", 5));

Ngoài ra, những gì về:

assert(numeric.Infinity, math.add(numeric.Infinity, 1));

Tự giải thích quá: bạn muốn phương pháp của bạn có thể giải quyết chính xác với vô hạn. Vượt quá vô hạn hoặc ném một ngoại lệ không phải là một hành vi dự kiến.

Hoặc có thể, tùy thuộc vào ngôn ngữ của bạn, điều này sẽ có ý nghĩa hơn?

/**
 * Ensures that when adding numbers which exceed the maximum value, the method
 * fails with OverflowException, instead of restarting at numeric.Minimum + 1.
 */
TestOverflow()
{
    UnitTest.ExpectException(ofType(OverflowException));

    numeric result = math.add(numeric.Maximum, 1));

    UnitTest.Fail("The tested code succeeded, while an OverflowException was
        expected.");
}

1

Đối với một chức năng rất đơn giản như add, kiểm tra có thể được coi là không cần thiết, nhưng khi các chức năng của bạn trở nên phức tạp hơn, nó càng trở nên rõ ràng hơn tại sao kiểm tra là cần thiết.

Hãy suy nghĩ về những gì bạn làm khi bạn lập trình (không có kiểm tra đơn vị). Thông thường bạn viết một số mã, chạy nó, thấy rằng nó hoạt động và chuyển sang điều tiếp theo, phải không? Khi bạn viết nhiều mã hơn, đặc biệt là trong một hệ thống / GUI / trang web rất lớn, bạn thấy rằng bạn phải thực hiện ngày càng nhiều hơn "chạy và xem nếu nó hoạt động". Bạn phải thử cái này và thử cái kia. Sau đó, bạn thực hiện một vài thay đổi và bạn phải thử lại những điều tương tự. Rõ ràng là bạn có thể tiết kiệm thời gian bằng cách viết các bài kiểm tra đơn vị sẽ tự động hóa toàn bộ phần "chạy và xem nếu nó hoạt động".

Khi các dự án của bạn trở nên lớn hơn và lớn hơn, số lượng những thứ bạn phải "chạy và xem nếu nó hoạt động" trở nên không thực tế. Vì vậy, cuối cùng bạn chỉ chạy và thử một vài thành phần chính của GUI / dự án và sau đó hy vọng mọi thứ khác đều ổn. Đây là một công thức cho thảm họa. Tất nhiên, với tư cách là một con người, bạn không thể liên tục kiểm tra mọi tình huống có thể xảy ra mà khách hàng của bạn có thể sử dụng nếu GUI được sử dụng bởi hàng trăm người. Nếu bạn đã kiểm tra đơn vị tại chỗ, bạn có thể chỉ cần chạy thử trước khi gửi phiên bản ổn định hoặc thậm chí trước khi cam kết vào kho lưu trữ trung tâm (nếu nơi làm việc của bạn sử dụng một thử nghiệm). Và, nếu có bất kỳ lỗi nào được tìm thấy sau này, bạn có thể thêm một bài kiểm tra đơn vị để kiểm tra nó trong tương lai.


1

Một trong những lợi ích của việc viết bài kiểm tra đơn vị là nó giúp bạn viết mã mạnh mẽ hơn bằng cách buộc bạn phải suy nghĩ về các trường hợp cạnh. Làm thế nào về việc kiểm tra một số trường hợp cạnh, như tràn số nguyên, cắt thập phân hoặc xử lý null cho các tham số?


0

Có thể bạn giả sử add () đã được thực hiện với lệnh ADD. Nếu một số lập trình viên cơ sở hoặc kỹ sư phần cứng thực hiện lại hàm add () bằng cách sử dụng ANDS / ORS / XORS, đảo ngược bit và dịch chuyển, bạn có thể muốn kiểm tra đơn vị nó theo lệnh ADD.

Nói chung, nếu bạn thay thế ruột của add () hoặc đơn vị được kiểm tra, bằng một số ngẫu nhiên hoặc trình tạo đầu ra, làm thế nào bạn biết rằng một cái gì đó đã bị hỏng? Mã hóa kiến ​​thức đó trong bài kiểm tra đơn vị của bạn. Nếu không ai có thể biết nếu nó bị hỏng, thì chỉ cần kiểm tra một số mã cho rand () và về nhà, công việc của bạn đã hoàn tất.


0

Tôi có thể đã bỏ lỡ nó trong số tất cả các câu trả lời, nhưng đối với tôi, ổ đĩa chính đằng sau Kiểm thử đơn vị ít nói về việc chứng minh tính đúng đắn của phương pháp ngày hôm nay nhưng nó chứng minh tính đúng đắn của phương pháp đó khi bạn thay đổi nó.

Thực hiện một chức năng đơn giản, như trả về số lượng vật phẩm trong một số bộ sưu tập. Ngày nay, khi danh sách của bạn dựa trên một cấu trúc dữ liệu nội bộ mà bạn biết rõ, bạn có thể nghĩ rằng phương pháp này quá rõ ràng đến nỗi bạn không cần thử nghiệm cho nó. Sau đó, trong vài tháng hoặc vài năm, bạn (hoặc người khác ) quyết định [s] thay thế cấu trúc danh sách nội bộ. Bạn vẫn cần biết rằng getCount () trả về giá trị chính xác.

Đó là nơi Bài kiểm tra đơn vị của bạn thực sự trở thành của riêng họ.

Bạn có thể thay đổi việc triển khai nội bộ mã của mình nhưng với bất kỳ người tiêu dùng nào của mã đó, kết quả vẫn giữ nguyê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.