2009-07-13 69 views
7

Bản sao có thể xảy ra:
What is unit testing and how do you do it?
What is unit testing?Kiểm tra đơn vị là gì?

tôi nhận ra rằng đến 95% của bạn, đây là một câu hỏi rất WTF.

So. Thử nghiệm đơn vị là gì? Tôi hiểu rằng về cơ bản bạn đang cố gắng tách biệt chức năng nguyên tử nhưng cách bạn có kiểm tra điều đó không? Khi nào cần thiết? Khi nào nó vô lý? Bạn có thể đưa ra một ví dụ không? (Tốt nhất là trong C? Tôi chủ yếu nghe về nó từ các nhà phát triển Java trên trang web này vì vậy có lẽ điều này là cụ thể cho các ngôn ngữ hướng đối tượng? Tôi thực sự không biết.)

Tôi biết nhiều lập trình viên thề bằng cách kiểm tra đơn vị tôn giáo. Tất cả là gì?

EDIT: Ngoài ra, tỷ lệ thời gian bạn thường dành để viết các bài kiểm tra đơn vị vào thời gian viết mã mới là bao nhiêu?

+0

Sao y bản sao. Chỉ cần tìm kiếm tràn ngăn xếp cho "Thử nghiệm đơn vị" là gì và bạn sẽ có đủ để nhai trong nhiều tháng. – womp

+0

@womp: Tôi vừa làm và tôi không thấy bất kỳ tựa game nào tương tự. –

+2

http://stackoverflow.com/search?q=unit+testing – Nifle

Trả lời

9

Tôi là Java bây giờ, trước đó C++, trước đó C. Tôi hoàn toàn tin rằng mọi công việc tôi đã làm, rằng bây giờ tôi không được nêu tên, đã được tăng cường bởi các chiến lược thử nghiệm tôi đã chọn. Skimping thử nghiệm đau.

Tôi chắc chắn rằng bạn kiểm tra mã bạn viết. Bạn sử dụng kỹ thuật nào? Ví dụ: bạn có thể ngồi trong trình gỡ lỗi và duyệt qua mã và xem điều gì xảy ra. Bạn có thể thực thi mã chống lại một số dữ liệu thử nghiệm mà ai đó đã cung cấp cho bạn. Bạn có thể đưa ra các đầu vào cụ thể bởi vì bạn biết rằng mã của bạn có một số hành vi thú vị cho các giá trị đầu vào nhất định. Giả sử nội dung của bạn sử dụng nội dung của người khác và chưa sẵn sàng, bạn giả lập mã của họ để mã của bạn có thể hoạt động với ít nhất một số câu trả lời giả mạo

Trong mọi trường hợp, bạn có thể ở một mức độ nào đó. Điều cuối cùng thú vị - bạn đang thử nghiệm rất nhiều trong sự cô lập, kiểm tra UNIT của bạn, ngay cả khi họ chưa sẵn sàng.

Ý kiến ​​của tôi:

1). Các thử nghiệm có thể dễ dàng chạy lại rất hữu ích - không bị trễ các lỗi. Trong thử nghiệm tương phản ngồi trong một trình gỡ lỗi là tâm-numbing.

2). Các hoạt động xây dựng các bài kiểm tra thú vị khi bạn viết mã của bạn, hoặc TRƯỚC KHI bạn viết mã của bạn làm cho bạn tập trung vào các trường hợp rìa của bạn. Những đầu vào không gây phiền toái và đầu vào rỗng, những thứ "tắt bởi một lỗi". Tôi nhận thấy mã tốt hơn sắp ra là kết quả của các bài kiểm tra đơn vị tốt.

3). Có một chi phí duy trì các bài kiểm tra. Nói chung nó có giá trị nó, nhưng đừng đánh giá thấp sự ảnh hưởng của việc giữ chúng hoạt động.

4). Có thể có xu hướng over-ephasise Unit Tests. Các lỗi thực sự thú vị có xu hướng ot creep khi các phần được tích hợp. Bạn thay thế thư viện mà bạn đã chế nhạo với điều thực sự và Lo! Nó không hoàn toàn làm những gì nó nói trên tin. Ngoài ra, vẫn còn một vai trò trong thử nghiệm thủ công hoặc khai thác.Các thử nghiệm con người sâu sắc tìm thấy khuyết tật đặc biệt.

+0

Độc đáo nói. Có lẽ bạn cũng có thể thêm vào "làm thế nào để kiểm tra ngay bây giờ": Bạn chỉ cần thêm (quá nhiều) ghi nhật ký và đọc qua trong khi chạy lại ứng dụng của mình, có thể mỗi lần nhấp/duyệt một cách xa thông qua ứng dụng của bạn, thiết lập dữ liệu phải tương tác trước khi bạn nhìn thấy, nếu nó hoạt động theo cách bạn mong đợi. (khẳng định thực tế dự kiến ​​:-)) – raoulsson

0

Trong lập trình máy tính, kiểm thử đơn vị là phương pháp xác minh phần mềm và xác thực trong đó một lập trình viên kiểm tra các đơn vị mã nguồn riêng lẻ phù hợp để sử dụng. Một đơn vị là phần nhỏ nhất có thể kiểm tra của một ứng dụng. Trong lập trình thủ tục, một đơn vị có thể là một chương trình, hàm, thủ tục, vv trong khi lập trình hướng đối tượng, đơn vị nhỏ nhất là một lớp, có thể thuộc về một lớp cơ sở/siêu hạng, lớp trừu tượng hoặc lớp dẫn xuất/lớp con.

http://en.wikipedia.org/wiki/Unit_testing

+0

Đó là khá thô lỗ. Tất nhiên tôi đã xem xét bài viết wikipedia về thử nghiệm đơn vị. Các câu hỏi tôi đã hỏi không được trả lời. Tôi muốn một số ví dụ từ thế giới thực. Tôi thu thập rằng đó là một vấn đề lớn và tôi muốn biết nó được sử dụng như thế nào trong thực tế. – Tyler

+0

Bạn cũng đã đọc các liên kết bên ngoài từ bài viết đó chưa? – Nifle

+0

@Nifle Có. Một lần nữa, không có cái nhìn sâu sắc của thế giới thực. Chỉ là lý thuyết. – Tyler

0

Ví dụ, nếu bạn có một lớp ma trận, bạn có thể có một bài kiểm tra đơn vị kiểm tra rằng

Matrix A = Matrix (.....); A.inverse() * A == Ma trận :: Bản sắc

1

Thử nghiệm đơn vị là một phần mềm khác mà bạn viết thực hiện mã chính của bạn để chấp nhận chức năng mong muốn.

Tôi có thể viết chương trình máy tính trông đẹp mắt, có các nút, trông giống như máy tính bất kỳ TI và có thể tạo ra 2 + 2 = 5. Có vẻ tốt đẹp, nhưng thay vì gửi mỗi lần lặp lại một số mã cho một người thử nghiệm, với danh sách kiểm tra dài, tôi, nhà phát triển có thể chạy một số kiểm tra đơn vị, mã hóa, tự động trên mã của tôi.

Về cơ bản, kiểm tra đơn vị phải được kiểm tra chính nó, bởi các đồng nghiệp, hoặc xem xét cẩn thận khác để trả lời "là thử nghiệm này những gì tôi muốn nó?"

Kiểm tra đơn vị sẽ có một bộ "Givens" hoặc "Đầu vào" và so sánh chúng với "Đầu ra" dự kiến.

Có, tất nhiên, các phương pháp khác nhau về cách thức, thời điểm và mức độ sử dụng các kiểm tra đơn vị (kiểm tra SO để biết một số câu hỏi theo các dòng này). Tuy nhiên, trong trường hợp cơ bản nhất, chúng là chương trình hoặc mô-đun có thể tải của một số chương trình khác, làm cho các xác nhận .

Ngữ pháp chuẩn cho thử nghiệm đơn vị có thể là có một dòng mã trông giống như sau: Assert.AreEqual(a, b).

Nội dung phương pháp thử nghiệm đơn vị có thể thiết lập đầu vào và đầu ra thực tế và so sánh với đầu ra mong muốn.

HelloWorldExample helloWorld = new HelloWorldExample(); 
string expected = "Hello World!"; 
string actual = helloWorld.GetString(); 

Assert.AreEqual(expected, actual); 

Nếu kiểm tra đơn vị của bạn được viết bằng ngôn ngữ của một khuôn khổ cụ thể (ví dụ như JUnit, NUnit, vv), các kết quả của từng phương pháp được đánh dấu như là một phần của một "chạy thử" sẽ là được tổng hợp thành một tập hợp các kết quả thử nghiệm, chẳng hạn như biểu đồ hình chấm màu đỏ đẹp cho thất bại và chấm màu xanh lục cho thành công và/hoặc tệp XML, v.v.

Để trả lời nhận xét mới nhất của bạn, "Lý thuyết" có thể cung cấp một số thấu hiểu thế giới thực. TDD, Test Driven Development, nói rất nhiều về thời gian và tần suất sử dụng các xét nghiệm. Trong dự án mới nhất của tôi, chúng tôi đã không tuân thủ TDD, nhưng chúng tôi chắc chắn đã sử dụng các bài kiểm tra đơn vị để xác minh rằng mã của chúng tôi đã làm những gì nó được cho là phải làm.

Giả sử bạn đã chọn triển khai Giao diện ô tô. Giao diện xe trông như thế này:

interface ICar 
{ 
    public void Accelerate(int delta); 
    public void Decelerate(int delta); 
    public int GetCurrentSpeed(); 
} 

Bạn chọn để thực hiện các giao diện ô tô trong lớp FordTaurus:

class FordTaurus : ICar 
{ 
    private int mySpeed; 
    public Accelerate(int delta) 
    { 
     mySpeed += delta; 
    } 
    public Decelerate(int delta) 
    { 
     mySpeed += delta; 
    } 
    public int GetCurrentSpeed() 
    { 
     return mySpeed; 
    } 
} 

Bạn đang giả định rằng để giảm tốc một FordTaurus, người ta phải vượt qua một giá trị âm. Tuy nhiên, giả sử bạn có một tập hợp các bài kiểm tra đơn vị được viết dựa trên giao diện Xe hơi và chúng trông như sau:

public static void TestAcceleration(ICar car) 
{ 
    int oldSpeed = car.GetCurrentSpeed(); 
    car.Accelerate(5); 
    int newSpeed = car.GetCurrentSpeed(); 
    Assert.IsTrue(newSpeed > oldSpeed); 
} 
public static void TestDeceleration(ICar car) 
{ 
    int oldSpeed = car.GetCurrentSpeed(); 
    car.Decelerate(5); 
    int newSpeed = car.GetCurrentSpeed(); 
    Assert.IsTrue(newSpeed < oldSpeed); 
} 

Bài kiểm tra cho bạn biết rằng có thể bạn đã triển khai giao diện xe không chính xác.

+0

Bạn có làm bài kiểm tra đơn vị trên mã của mình không? Đối với mọi tham số có thể cho mọi chức năng bạn viết? Tỷ lệ thời gian dành cho việc kiểm tra đơn vị viết thời gian dành cho việc viết mã mới là bao nhiêu? – Tyler

+1

Câu trả lời ngắn gọn: nó phụ thuộc. Kiểm tra các câu trả lời khác của SO cho câu hỏi này. Nếu tôi không cần VÀ tôi không muốn, tôi thì không. Thỉnh thoảng tôi được yêu cầu. Thỉnh thoảng tôi cảm thấy nó sẽ hữu ích. Thời gian khác, nó sẽ là vô lý nếu tôi đã làm. Nhiều người sẽ cho bạn biết rằng nếu mã của bạn được chuyển đi hoặc được tái đăng ký, hoặc đã được tái cấu trúc, một bộ kiểm tra có thể theo thứ tự. – maxwellb

+1

Trả lời câu hỏi về tỷ lệ thời gian viết bài kiểm tra để viết mã mới ngụ ý việc áp dụng một phương pháp cụ thể để viết mã đã nói. Nếu bạn đang học trung học, và chỉ muốn có được mã hóa kinh nghiệm, hãy hiểu rằng việc áp dụng bất kỳ phương pháp luận nào cũng đòi hỏi thời gian và lập kế hoạch. Nếu bạn muốn thực hành TDD, hãy đọc một cuốn sách về chủ đề và làm việc qua từng chương, nói một tuần một lần (lưu ý: Tôi không có kiến ​​thức đặc biệt về những cuốn sách tồn tại về chủ đề này). Hãy tự hỏi mình "Mục tiêu của tôi là gì?". Nếu bạn đang làm việc theo cách của bạn thông qua một API cụ thể, nhận được sâu vào các xét nghiệm có thể không phải là câu trả lời. :-) – maxwellb

1

Điểm theo điểm:

1) Thử nghiệm đơn vị là gì?

Thử nghiệm đơn vị là kiểm thử phần mềm được thiết kế để kiểm tra một đơn vị chức năng riêng biệt của phần mềm.

2) Tôi hiểu rằng về cơ bản bạn đang cố gắng cách ly chức năng nguyên tử nhưng làm thế nào để bạn thử nghiệm cho điều đó?

Kiểm tra đơn vị thực sự là một cách hay để thực thi các nguyên tắc thiết kế nhất định; một trong những khía cạnh của chúng là chúng thực sự tạo ra một hiệu ứng tinh tế nhưng quan trọng đối với việc thiết kế mã. Thiết kế cho thử nghiệm là một điều quan trọng; có thể kiểm tra (hoặc không) một số mã nhất định có thể rất quan trọng; khi các xét nghiệm đơn vị đang được sử dụng, các thiết kế có xu hướng di chuyển về phía "nguyên tử hơn" của quang phổ.

3) Khi nào cần thiết?

Có rất nhiều ý kiến ​​khác nhau về vấn đề này. Một số người nói nó luôn luôn cần thiết, một số người nói nó hoàn toàn không cần thiết. Tôi cho rằng hầu hết các nhà phát triển có kinh nghiệm với Unit Testing sẽ nói rằng Unit Test là cần thiết cho bất kỳ mã đường dẫn quan trọng nào có thiết kế phù hợp với Unit Testing (tôi biết nó là một chút thông tư, nhưng xem # 2 ở trên).

  1. Khi nào nó vô lý? Bạn có thể đưa ra một ví dụ không?

Nói chung, quá mức là nơi bạn có được kết thúc vô lý của quang phổ. Ví dụ, nếu bạn có một lớp 3D Vector có accessors cho mỗi thành phần vô hướng, việc kiểm tra đơn vị cho mỗi accessar vô hướng xác nhận phạm vi đầy đủ của các đầu vào và xác minh các giá trị cho mỗi đầu vào sẽ được coi là một chút quá mức của một số người. Mặt khác, điều quan trọng cần lưu ý là ngay cả những tình huống đó cũng có thể hữu ích để kiểm tra.

  1. Tôi chủ yếu nghe về nó từ các nhà phát triển Java trên trang web này vì vậy có thể điều này dành riêng cho các ngôn ngữ hướng đối tượng?

Không, nó thực sự áp dụng cho bất kỳ phần mềm nào. Phương pháp kiểm tra đơn vị đã đến với sự trưởng thành với môi trường Java, nhưng nó thực sự áp dụng cho bất kỳ ngôn ngữ hoặc môi trường nào.

  1. Tất cả là gì?

Kiểm tra đơn vị ở mức rất cơ bản, tất cả về xác minh và xác thực rằng hành vi được mong đợi từ một đơn vị mã là THỰC TẾ mã nào.

2

Các đơn giản nhất không định nghĩa/kỹ thuật tôi có thể đưa ra cách tự động để kiểm tra các phần của mã ...

tôi sử dụng nó và yêu nó ... nhưng không phải tôn giáo, Một trong những khoảnh khắc đáng tự hào nhất của tôi trong thử nghiệm đơn vị là tính toán lãi suất mà tôi đã làm cho ngân hàng, cực kỳ phức tạp và tôi chỉ có một lỗi và không có kiểm tra đơn vị cho trường hợp đó ... ngay khi tôi thêm trường hợp và sửa mã của tôi, nó hoàn hảo.

Vì vậy, lấy ví dụ đó tôi đã có một cuộc gọi lớp InterestCalculation và nó có thuộc tính cho tất cả các đối số và một phương pháp công khai Calculate() Có một số bước để tính toán và nếu tôi ở đâu để thử và viết toàn bộ nội dung trong một và tôi chỉ cần kiểm tra kết quả của tôi, nó sẽ quá lấn át để tìm ra chỗ của tôi ở đâu ... Vì vậy, tôi đã thực hiện từng bước tính toán và tạo ra một phương pháp riêng và kiểm tra đơn vị cho tất cả các trường hợp khác nhau. (Một số người sẽ cho bạn biết để chỉ thử nghiệm các phương pháp công cộng, nhưng trong trường hợp này nó làm việc tốt hơn cho tôi ...) Một ví dụ về những phương pháp riêng là:

Phương pháp:

/// <summary> 
    /// 
    /// </summary> 
    /// <param name="effectiveDate"></param> 
    /// <param name="lastCouponDate"></param> 
    /// <returns></returns> 
    private Int32 CalculateNumberDaysSinceLastCouponDate(DateTime effectiveDate, DateTime lastCouponDate) 
    { 
     Int32 result = 0; 

     if (lastCouponDate.Month == effectiveDate.Month) 
     { 
      result = this._Parameters.DayCount.GetDayOfMonth(effectiveDate) - lastCouponDate.Day; 
     } 
     else 
     { 
      result = this._Parameters.DayCount.GetNumberOfDaysInMonth(lastCouponDate) 
       - lastCouponDate.Day + effectiveDate.Day; 
     } 

     return result; 
    } 

thử nghiệm phương pháp:

Lưu ý: tôi sẽ đặt tên họ khác nhau bây giờ, thay vì con số tôi sẽ về cơ bản đưa các bản tóm tắt vào tên phương thức.

/// <summary> 
    ///A test for CalculateNumberDaysSinceLastCouponDate 
    ///</summary> 
    [TestMethod()] 
    [DeploymentItem("WATrust.CAPS.DataAccess.dll")] 
    public void CalculateNumberDaysSinceLastCouponDateTest1() 
    { 
     AccruedInterestCalculationMonthly_Accessor target = new AccruedInterestCalculationMonthly_Accessor(); 
     target._Parameters = new AccruedInterestCalculationMonthlyParameters(); 
     target._Parameters.DayCount = new DayCount(13); 
     DateTime effectiveDate = DateTime.Parse("04/22/2008"); 
     DateTime lastCouponDate = DateTime.Parse("04/15/2008"); 
     int expected = 7; 
     int actual; 

     actual = target.CalculateNumberDaysSinceLastCouponDate(effectiveDate, lastCouponDate); 

     Assert.AreEqual(expected, actual); 

     WriteToConsole(expected, actual); 
    } 

    /// <summary> 
    ///A test for CalculateNumberDaysSinceLastCouponDate 
    ///</summary> 
    [TestMethod()] 
    [DeploymentItem("WATrust.CAPS.DataAccess.dll")] 
    public void CalculateNumberDaysSinceLastCouponDateTest2() 
    { 
     AccruedInterestCalculationMonthly_Accessor target = new AccruedInterestCalculationMonthly_Accessor(); 
     target._Parameters = new AccruedInterestCalculationMonthlyParameters(); 
     target._Parameters.DayCount = new DayCount((Int32) 
      DayCount.DayCountTypes.ThirtyOverThreeSixty); 

     DateTime effectiveDate = DateTime.Parse("04/10/2008"); 
     DateTime lastCouponDate = DateTime.Parse("03/15/2008"); 
     int expected = 25; 
     int actual; 

     actual = target.CalculateNumberDaysSinceLastCouponDate(effectiveDate, lastCouponDate); 

     Assert.AreEqual(expected, actual); 

     WriteToConsole(expected, actual); 
    }    

nó là vô lý ở đâu?

Vâng, mỗi bạn ... Bạn càng làm điều đó, bạn sẽ thấy nó hữu ích ở đâu và có vẻ "vô lý" nhưng cá nhân tôi không dùng nó để kiểm tra Cơ sở dữ liệu của tôi Hầu hết những người thử nghiệm đơn vị hardcore sẽ ... Theo nghĩa là tôi có một kịch bản để xây dựng lại lược đồ cơ sở dữ liệu, tái cơ sở dữ liệu với dữ liệu thử nghiệm, v.v. Tôi thường viết một phương pháp thử nghiệm đơn vị để gọi phương thức DataAccess của tôi và gắn nhãn nó với hậu tố Debug như thế này: FindLoanNotes_Debug() và tôi đã đặt System.Diagnostics.Debugger.Break() vì vậy nếu tôi chạy chúng trong chế độ gỡ lỗi, tôi có thể tự kiểm tra kết quả của mình.

1

Vì vậy, bạn muốn có ví dụ? Học kỳ trước tôi đã tham gia một khóa học trình biên dịch. Trong đó chúng tôi đã phải viết một cấp phát đăng ký. Nói một cách đơn giản, chương trình của tôi có thể được tóm tắt như sau:

Đầu vào: Một tệp được viết bằng ngôn ngữ lập trình giả tạo cho sách giáo khoa của tôi. Các hướng dẫn trong tập tin có tên đăng ký như "r <number>". Vấn đề là chương trình sử dụng nhiều thanh ghi như nó cần, thường lớn hơn số lượng thanh ghi trên máy đích.

Đầu ra: Một tệp khác được viết bằng ILOC. Lần này, các hướng dẫn được viết lại để nó sử dụng số lượng đăng ký tối đa chính xác được cho phép.

Để viết chương trình này, tôi phải tạo một lớp có thể phân tích cú pháp tệp ILOC. Tôi đã viết một loạt các bài kiểm tra cho lớp đó. Dưới đây là các bài kiểm tra của tôi (tôi thực sự đã có nhiều hơn, nhưng đã loại bỏ chúng để giúp rút ngắn điều này. Tôi cũng đã thêm một số ý kiến ​​để giúp bạn đọc nó). Tôi đã làm dự án trong C++, vì vậy tôi đã sử dụng khung kiểm tra C++ của Google (googletest) có vị trí here.

Trước khi hiển thị mã cho bạn ... hãy để tôi nói điều gì đó về cấu trúc cơ bản. Về cơ bản, có một lớp kiểm tra. Bạn có thể đặt một loạt các công cụ thiết lập chung trong lớp thử nghiệm. Sau đó, có các macro thử nghiệm được gọi là TEST_F. Khung kiểm tra chọn lên trên và hiểu rằng chúng cần phải được chạy thử nghiệm. Mỗi TEST_F có 2 đối số, tên lớp kiểm tra và tên của bài kiểm tra (nên rất mô tả ... theo cách đó nếu kiểm tra không thành công, bạn biết chính xác những gì không thành công).Bạn sẽ thấy cấu trúc của mỗi thử nghiệm là tương tự: (1) thiết lập một số công cụ ban đầu, (2) chạy phương pháp bạn đang thử nghiệm, (3) xác minh đầu ra là chính xác. Cách bạn kiểm tra (3) là sử dụng các macro như EXPECT_ *. EXPECT_EQ(expected, result) kiểm tra xem result có bằng expected không. Nếu không, bạn nhận được một thông báo lỗi hữu ích như "kết quả là blah, nhưng mong đợi Blah". Đây là mã (Tôi hy vọng điều này không quá khó hiểu ... nó chắc chắn không phải là một ví dụ ngắn hoặc dễ dàng, nhưng nếu bạn dành thời gian bạn sẽ có thể theo dõi và có được hương vị chung của nó như thế nào công trinh).

// Unit tests for the iloc_parser.{h, cc} 

#include <fstream> 
#include <iostream> 
#include <gtest/gtest.h> 
#include <sstream> 
#include <string> 
#include <vector> 

#include "iloc_parser.h" 

using namespace std; 

namespace compilers { 
// Here is my test class 
class IlocParserTest : public testing::Test { 
protected: 
    IlocParserTest() {} 
    virtual ~IlocParserTest() {} 

    virtual void SetUp() { 
    const testing::TestInfo* const test_info = 
     testing::UnitTest::GetInstance()->current_test_info(); 
    test_name_ = test_info->name(); 
    } 

    string test_name_; 
}; 

// Here is a utility function to help me test 
static void ReadFileAsString(const string& filename, string* output) { 
    ifstream in_file(filename.c_str()); 
    stringstream result(""); 
    string temp; 
    while (getline(in_file, temp)) { 
    result << temp << endl; 
    } 
    *output = result.str(); 
} 

// All of these TEST_F things are macros that are part of the test framework I used. 
// Just think of them as test functions. The argument is the name of the test class. 
// The second one is the name of the test (A descriptive name so you know what it is 
// testing). 
TEST_F(IlocParserTest, ReplaceSingleInstanceOfSingleCharWithEmptyString) { 
    string to_replace = "blah,blah"; 
    string to_find = ","; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblah", to_replace); 
} 

TEST_F(IlocParserTest, ReplaceMultipleInstancesOfSingleCharWithEmptyString) { 
    string to_replace = "blah,blah,blah"; 
    string to_find = ","; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblahblah", to_replace); 
} 

TEST_F(IlocParserTest, 
     ReplaceMultipleInstancesOfMultipleCharsWithEmptyString) { 
    string to_replace = "blah=>blah=>blah"; 
    string to_find = "=>"; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblahblah", to_replace); 
} 

// This test was suppsoed to strip out the "r" from register 
// register names in the ILOC code. 
TEST_F(IlocParserTest, StripIlocLineLoadI) { 
    string iloc_line = "loadI\t1028\t=> r11"; 
    IlocParser::StripIlocLine(&iloc_line); 
    EXPECT_EQ("loadI\t1028\t 11", iloc_line); 
} 

// Here I make sure stripping the line works when it has a comment 
TEST_F(IlocParserTest, StripIlocLineSubWithComment) { 
    string iloc_line = "sub\tr12, r10\t=> r13 // Subtract r10 from r12\n"; 
    IlocParser::StripIlocLine(&iloc_line); 
    EXPECT_EQ("sub\t12 10\t 13 ", iloc_line); 
} 


// Here I make sure I can break a line up into the tokens I wanted. 
TEST_F(IlocParserTest, TokenizeIlocLineNormalInstruction) { 
    string iloc_line = "sub\t12 10\t 13\n"; // already stripped 
    vector<string> tokens; 
    IlocParser::TokenizeIlocLine(iloc_line, &tokens); 
    EXPECT_EQ(4, tokens.size()); 
    EXPECT_EQ("sub", tokens[0]); 
    EXPECT_EQ("12", tokens[1]); 
    EXPECT_EQ("10", tokens[2]); 
    EXPECT_EQ("13", tokens[3]); 
} 


// Here I make sure I can create an instruction from the tokens 
TEST_F(IlocParserTest, CreateIlocInstructionLoadI) { 
    vector<string> tokens; 
    tokens.push_back("loadI"); 
    tokens.push_back("1"); 
    tokens.push_back("5"); 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_TRUE(IlocParser::CreateIlocInstruction(tokens, 
               &instruction)); 
    EXPECT_EQ(IlocInstruction::LOADI, instruction.op_code()); 
    EXPECT_EQ(2, instruction.num_operands()); 
    IlocInstruction::OperandList::const_iterator it = instruction.begin(); 
    EXPECT_EQ(1, *it); 
    ++it; 
    EXPECT_EQ(5, *it); 
} 

// Making sure the CreateIlocInstruction() method fails when it should. 
TEST_F(IlocParserTest, CreateIlocInstructionFromMisspelledOp) { 
    vector<string> tokens; 
    tokens.push_back("ADD"); 
    tokens.push_back("1"); 
    tokens.push_back("5"); 
    tokens.push_back("2"); 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_FALSE(IlocParser::CreateIlocInstruction(tokens, 
              &instruction)); 
    EXPECT_EQ(0, instruction.num_operands()); 
} 

// Make sure creating an empty instruction works because there 
// were times when I would actually have an empty tokens vector. 
TEST_F(IlocParserTest, CreateIlocInstructionFromNoTokens) { 
    // Empty, which happens from a line that is a comment. 
    vector<string> tokens; 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_TRUE(IlocParser::CreateIlocInstruction(tokens, 
               &instruction)); 
    EXPECT_EQ(IlocInstruction::NONE, instruction.op_code()); 
    EXPECT_EQ(0, instruction.num_operands()); 
} 

// This was a function that helped me generate actual code 
// that I could output as a line in my output file. 
TEST_F(IlocParserTest, MakeIlocLineFromInstructionAddI) { 
    IlocInstruction instruction(IlocInstruction::ADDI); 
    vector<int> operands; 
    operands.push_back(1); 
    operands.push_back(2); 
    operands.push_back(3); 
    instruction.CopyOperandsFrom(operands); 
    string output; 
    EXPECT_TRUE(IlocParser::MakeIlocLineFromInstruction(instruction, &output)); 
    EXPECT_EQ("addI r1, 2 => r3", output); 
} 

// This test actually glued a bunch of stuff together. It actually 
// read an input file (that was the name of the test) and parsed it 
// I then checked that it parsed it correctly. 
TEST_F(IlocParserTest, ParseIlocFileSimple) { 
    IlocParser parser; 
    vector<IlocInstruction*> lines; 
    EXPECT_TRUE(parser.ParseIlocFile(test_name_, &lines)); 
    EXPECT_EQ(2, lines.size()); 

    // Check first line 
    EXPECT_EQ(IlocInstruction::ADD, lines[0]->op_code()); 
    EXPECT_EQ(3, lines[0]->num_operands()); 
    IlocInstruction::OperandList::const_iterator operand = lines[0]->begin(); 
    EXPECT_EQ(1, *operand); 
    ++operand; 
    EXPECT_EQ(2, *operand); 
    ++operand; 
    EXPECT_EQ(3, *operand); 

    // Check second line 
    EXPECT_EQ(IlocInstruction::LOADI, lines[1]->op_code()); 
    EXPECT_EQ(2, lines[1]->num_operands()); 
    operand = lines[1]->begin(); 
    EXPECT_EQ(5, *operand); 
    ++operand; 
    EXPECT_EQ(10, *operand); 

    // Deallocate memory 
    for (vector<IlocInstruction*>::iterator it = lines.begin(); 
     it != lines.end(); 
     ++it) { 
    delete *it; 
    } 
} 

// This test made sure I generated an output file correctly. 
// I built the file as an in memory representation, and then 
// output it. I had a "golden file" that was supposed to represent 
// the correct output. I compare my output to the golden file to 
// make sure it was correct. 
TEST_F(IlocParserTest, WriteIlocFileSimple) { 
    // Setup instructions 
    IlocInstruction instruction1(IlocInstruction::ADD); 
    vector<int> operands; 
    operands.push_back(1); 
    operands.push_back(2); 
    operands.push_back(3); 
    instruction1.CopyOperandsFrom(operands); 
    operands.clear(); 
    IlocInstruction instruction2(IlocInstruction::LOADI); 
    operands.push_back(17); 
    operands.push_back(10); 
    instruction2.CopyOperandsFrom(operands); 
    operands.clear(); 
    IlocInstruction instruction3(IlocInstruction::OUTPUT); 
    operands.push_back(1024); 
    instruction3.CopyOperandsFrom(operands); 

    // Propogate lines with the instructions 
    vector<IlocInstruction*> lines; 
    lines.push_back(&instruction1); 
    lines.push_back(&instruction2); 
    lines.push_back(&instruction3); 

    // Write out the file 
    string out_filename = test_name_ + "_output"; 
    string golden_filename = test_name_ + "_golden"; 
    IlocParser parser; 
    EXPECT_TRUE(parser.WriteIlocFile(out_filename, lines)); 

    // Read back output file and verify contents are as expected. 
    string golden_file; 
    string out_file; 
    ReadFileAsString(golden_filename, &golden_file); 
    ReadFileAsString(out_filename, &out_file); 
    EXPECT_EQ(golden_file, out_file); 
} 
} // namespace compilers 

int main(int argc, char** argv) { 
    // Boiler plate, test initialization 
    testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS(); 
} 

Sau khi tất cả được nói và thực hiện ... TẠI SAO TÔI LÀM NÀY !? Vâng đầu tiên của tất cả. Tôi đã viết các bài kiểm tra từng bước khi tôi chuẩn bị viết từng đoạn mã. Nó đã giúp tôi yên tâm rằng mã tôi đã viết đã hoạt động đúng cách. Sẽ thật điên rồ khi viết tất cả mã của tôi và sau đó chỉ cần thử nó trên một tập tin và xem chuyện gì đã xảy ra. Có rất nhiều lớp, làm thế nào tôi có thể biết một lỗi sẽ đến từ đâu trừ khi tôi có từng mảnh nhỏ được thử nghiệm trong sự cô lập?

NHƯNG ... QUAN TRỌNG NHẤT! Thử nghiệm không thực sự về việc bắt các lỗi ban đầu trong mã của bạn ... đó là về việc bảo vệ bản thân khỏi vô tình phá vỡ mã của bạn. Mỗi lần tôi refactored hoặc thay đổi lớp IlocParser của tôi, tôi đã tự tin tôi đã không thay đổi nó một cách xấu bởi vì tôi có thể chạy thử nghiệm của tôi (trong vài giây) và thấy rằng tất cả các mã vẫn hoạt động như mong đợi. Đó là việc sử dụng các bài kiểm tra đơn vị tuyệt vời.

Có vẻ như họ mất quá nhiều thời gian ... nhưng cuối cùng, họ giúp bạn tiết kiệm thời gian theo dõi lỗi vì bạn đã thay đổi một số mã và không biết điều gì đã xảy ra. Họ là một cách hữu ích để xác minh rằng các đoạn mã nhỏ đang làm những gì họ có nghĩa vụ phải làm, và chính xác.

+0

Wow. Cảm ơn bạn đã đặt điều này với nhau. Điều này rất, rất hữu ích. – Tyler