- vừa được xem lúc

Using Copilot to generate Unit Test

0 0 17

Người đăng: ttuan

Theo Viblo Asia

🇬🇧 English version 🇬🇧

Image Source

Introduction

Sau hơn 2 năm ra mắt, Copilot chắc hẳn không còn xa lạ gì với anh em cốt đờ. Với những lời tán dương có cánh như change the game, revolutionizing developer collaboration, reshape the way we write code, intelligent suggestions, ... chắc hẳn ai trong chúng ta cũng đôi chút tò mò, xem thực sự Copilot có thể làm được gì?

Liệu chỉ với 10$/ tháng, ta có thể trở thành lập trình viên 2 nút (Tab + Enter)? Hay xa hơn, liệu anh em cốt đờ chúng ta có thể bị đào thải bởi AI trong tương lai? Mình cũng có khá nhiều câu hỏi đặt ra trong đầu.

Rất may mắn là công ty cũng không đứng ngoài xu thế, đã cho anh em có cơ hội trải nghiệm Copilot, nên phần nào những câu hỏi của mình cũng đã được giải đáp.

Trong quá trình demo, mình đã thử áp dụng cho dự án hiện tại (đang trong giai đoạn viết Unit Test), nên trong bài viết này, chúng ta sẽ cùng nhau đi tìm hiểu về Copilot, và xem thử khả năng viết UT của nó có thực sự bá đạo như lời đồn không nhé.

Nội dung chính:

  • Copilot có những tính năng gì?
  • Copilot support chúng ta như thế nào trong việc viết Unit Test?
  • Rút ra nhận xét.

OK! Gét gô! 🚀

Copilot features

Trước tiên, anh em hãy đảo qua một số tính năng của Copilot, để xem thiên hạ đang dùng thằng personal AI pair programmer này như thế nào nha.

  1. Suggest code: Tính năng mạnh nhất. Copilot có khả năng đọc, hiểu những gì bạn đang viết, từ đó đưa ra suggested code phù hợp - Coding không còn cô đơn, vì bạn đã có côpilot.
  2. Explain code: Giải thích chi tiết xem đoạn code này làm gì, step by step luôn. Tính năng này rất hữu ích khi chúng ta muốn đọc hiểu code cũ, hoặc muốn hiểu về 1 solution nào đó trên StackOverflow.
  3. Copilot Chat: Tư vấn và giải đáp thắc mắc tại khung chat của Editor luôn, không cần mất thời gian switch qua lại giữa browser và editor như khi dùng ChatGPT hoặc các tool AI khác. Đặc biệt, do được tích hợp vào editor, nên Copilot Chat có thể hiểu được context dự án tốt hơn, từ đó đưa ra câu trả lời phù hợp nhất.
  4. Copilot Labs: Dự án độc lập với Github Copilot, nhưng được rất nhiều anh em sử dụng bởi những tính năng rất hữu ích của nó: Refactor code, Fix bug, Debug, Comment code, ...
  5. Others
    • Translate từ ngôn ngữ này sang ngôn ngữ khác.
    • Explore library: Dùng khi chúng ta muốn học hỏi 1 ngôn ngữ/ thư viện mới. Copilot có thể suggest cú pháp, methods, .... phù hợp với ngôn ngữ/ thư viện hiện tại.
    • Những dự án đang được thử nghiệm: Copilot CLI, Copilot for PR, ...
    • Joke =)): Mình chỉ biết có tính năng này khi đọc những bài review về Copilot. Hãy thử gõ "Database tables walk into a bar" vào editor xem sao =))

Với Generating Unit Test, Github Copilot team cũng đang triển khai 1 project rất đáng mong chờ:

Automate automated testing.

Missed a test? GitHub Copilot can point out missing unit tests and generate new test cases for you after every change.

Và trong khi chờ đợi tính năng này, hôm nay chúng ta sẽ xem thử xem, với phiên bản hiện tại, Copilot có thể giúp chúng ta được phần nào khi viết test không nhé 😄

We don't have time to write tests ..

Copilot and Unit Tests

Để minh họa cho bài viết này, mình có tạo 1 Rails project đơn giản, với 2 models chính là: AuthorPost, sử dụng các thư viện phổ biến khi viết test như rspec, factory-bot, shoulda-matcherfaker .

Trong bài viết này, mình dùng VSCode. Cách cài đặt Copilot trên VSCode, bạn có thể tham khảo ở đây.

Để generate test bằng Copilot trên VSCode có 3 cách:

  1. Sử dụng Cmd + Shift + P (Hoặc Ctrl + Shift + P trên Windows) + Generate Tests

  2. Viết prompt hoặc code như bình thường, Copilot sẽ show gợi ý. Mình sẽ chọn Accept hoặc Discard.

  3. Sử dụng Copilot Chat, yêu cầu Copilot Chat generate tests.

Trong bài viết này, mình sẽ sử dụng cả 3 cách, để compare thử xem ưu/ nhược điểm của từng cách, và nên dùng chúng trong trường hợp nào.

Model

Copilot có thể viết test cho tất cả các đoạn code thông thường ở trong model không?

Để trả lời cho câu hỏi này, ta sẽ thử liệt kê xem chúng ta thường viết gì trong 1 file model?

Associations, Validations, Scopes, Instance methods, Class methods, Callbacks, Enum, Delegate, Custom Validations, ...

Tùy từng dự án, có thể có thêm các đoạn code khác, nhưng trong quá trình làm việc của mình, đại đa số các model sẽ chứa thông tin dạng như thế này.

Yoh. Giờ chúng ta sẽ bắt đầu thử nghiệm:

  1. Add thêm associations, validations, scopes, .... vào trong file model.
  2. Generate tests sử dụng Copilot.

Associations

Sau khi thêm associations has_many :posts, belongs_to :author vào model tương ứng, đến giờ cho Copilot làm việc rồi!!

Nice

  • Điểm cộng:
    • Code được generate khá nhanh, developer có thể review trước.
    • Biết tự thêm vào file spec tương ứng.
    • Biết sử dụng cú pháp của shoulda-matcher 👍
  • Điểm trừ:
    • Khi bấm apply, Copilot không nhận diện được existed content, mà sẽ append vào cuối file đó luôn.

Đối với belongs_to association, mình tự vào file test và viết test manual.

  • Điểm trừ:
    • Code gợi ý lần đầu không liên quan gì =))
  • Điểm cộng:
    • Sau khi gõ describe thì Copilot đã gợi ý khá tốt, ra test cho associations và phần còn lại.

Scopes

Mình lựa chọn 2 scopes để test thử

  • Lấy ra các posts được tạo trong khoảng thời gian từ start_date -> end_date
  • Order posts by raw ids.

Lần này mình dùng Copilot Chat để generate test.

Hmm. Mặc dù "selected code" là 2 scopes, nhưng không hiểu sao Copilot chỉ tạo test cho 1 scope. 🤔

Với scope còn lại, mình phải gõ thử bằng tay. Tuy nhiên, Copilot cũng hiểu được bối cảnh, và generate test khá ok.

  • Điểm cộng:
    • Về format, test được generate khá chuẩn, có cả dữ liệu includenot include
    • Biết fake data sử dụng cú pháp của FactoryBot.
  • Điểm trừ:
    • Không generate test cho hết cho "selected code".
    • Và một điểm trừ khá lớn, đó là khi mình chạy test thì test case returns posts created between the given dates bị fail =)) (Nguyên nhân là do Time dependency - lag time giữa thời điểm tạo dữ liệu test và thời điểm chạy test case).

Như vậy, chúng ta có thể thấy rằng, Copilot có thể generate ra tests, nhưng có đúng không, hoặc chạy được không thì chưa chắc =))

Validations

Đối với phần test cho Validations, mình lựa chọn 1 validation đơn giản cho trường name, và 1 email regex để validate email.

VALID_EMAIL_REGEX= /^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})$/i validates :email , presence: true,uniqueness: {case_sensetive: false}, format:{with:VALID_EMAIL_REGEX, multiline: true} validates :name, presence: true, length: { minimum: 3, maximum: 50 }

Tới đây thì mình bắt đầu thấy có chút vấn đề. Copilot suggest một số test basic. Phần test cho email thì bỏ trống. Mặc dù mình đã tab để accept kết quả hiện tại, nhưng Copilot vẫn không show thêm gợi ý gì.

Dù đã viết thử comment: # Read VALID_EMAIL_REGEX in author.rb model, generate tests for email validations based this regex, vẫn không có test mới nào được generate ra.

Mình phải request qua Copilot Chat mới nhận lại được kết quả.

Đoạn này mình nghĩ nên cộng điểm cho Copilot, vì nó biết sử dụng build(:author) thay vì create(:author), sẽ tiết kiệm được chút thời gian chạy test. ⏱️

Others

Mình có test thử với những phần còn lại: instance methods, class methods, enum, delegation, ... thì Copilot có thể đáp ứng được các usecases đơn giản. Đối với một số case phức tạp hơn, hoặc "hiếm" gặp hơn, thì Copilot không đáp ứng được yêu cầu:

def normalize_content convert = {"\u309B" => "\u3099", "\u309C" => "\u309A"} content.gsub(/\u309B|\u309C/, convert).gsub(/\p{L}[\u3099|\u309A]/) do |str| str.unicode_normalize(:nfkc) end
end

Ví dụ, mình có một function như trên, làm nhiệm vụ normalize 1 vài kí tự đặc biệt. Tuy nhiên, khi viết test thì Copilot đã lấy một ví dụ không liên quan tới logic của function normalize_content. Có thể là do Copilot generate test bằng cách "đoán" từ tên hàm❓

Như vậy, chúng ta có thể rút ra một vài nhận xét:

  • Copilot có khả năng generate ra những test đơn giản, phổ biến. Giúp đỡ chúng ta tốt trong việc typing repeated snippet.
  • Đối với một số đoạn code có logic phức tạp, Copilot thường không suggest hoặc nếu suggest thì đưa ra những đoạn code sai/ không liên quan. Với những case như vậy, chúng ta cần viết comment để guide Copilot, break nhỏ function hoặc phải tự code và để Copilot suggest phần còn lại.
  • Chúng ta vẫn cần verify lại tính đúng đắn của generated code.

Controller

Để demo cho phần generate test cho controller, mình có thử với hàm index, lấy ra 1 list các published posts và paginate cho nó.

Ban đầu, do thiếu context, Copilot sẽ chỉ generate ra test cases đơn giản (Kiểm tra request successful). Tuy nhiên, sau khi được guide theo ý tưởng viết test của mình, Copilot đã bắt kịp rất nhanh.

  • Copilot có khả năng học rất nhanh. Chúng ta chỉ cần "làm mẫu" một lần, là nó có thể học được style code/ cách đặt tên/ cách comment và cả phong cách viết test. Điều này hỗ trợ rất tốt trong việc đảm bảo tính nhất quán của source code.
  • Khi viết test, Copilot có thể chủ động áp dụng các kĩ thuật Mock/Stub.

Cá nhân mình thấy Copilot support tốt nhất cho phần viết test Controller. Lý do là vì test của controller có tính lặp lại theo một format chung. Mà đây lại đúng là sở trường của Copilot - Xử lý repeated snippet.

Workers/ Services and others

Đối với Worker và Services, chúng ta quay trở lại bài toán viết Unit Test cho các class Ruby thông thường.

Ở đây mình có viết thử 1 đoạn code, gọi lên dev.to để lấy bài post mới và import vào database.

Logic cũng không có gì phức tạp, nên Copilot xử lý khá ổn, đặc biệt trong khoản mock/stub.

Real-Life project

Trong quá trình làm việc thực tế, mình thường gặp 1 số vấn đề khi sử dụng Copilot generate test cho services/workers/ custom libraries/....

  1. Nếu business logic trong các class này phức tạp, Copilot gặp khó trong việc generate ra test cases cho cả file. Dẫn tới trường hợp lack cases, coverage không đạt yêu cầu.
  2. Copilot generate test tốt trong trường hợp mình viết class từ đầu. Đối với những trường hợp sửa code cũ (fix bugs, update logic business, refactor, ...) khi được yêu cầu generate test, nó thường tạo test cho cả file, chứ không chỉ tập trung test phần mình vừa thêm vào.

Để xử lý những vấn đề này, hãy thử:

  1. Chia logic ra thành các function nhỏ: Copilot generate test tốt hơn với những đoạn code mà logic không quá phức tạp.
  2. Đừng đuổi theo Copilot, hãy để Copilot theo đuổi bạn: Về cơ bản, Copilot cũng chỉ là 1 tool hỗ trợ viết code thôi, phần quan trọng nhất vẫn là ở bạn. Vì thế, không ỷ lại, không chờ đợi, hãy cứ bắt tay vào viết code, và để Copilot follow/support bạn .
  3. Đừng tin tưởng 100% vào Copilot, đặc biệt là khi nó suggest một đoạn code dài =))

Summary

Copilot đang tỏ ra là một trợ thủ đắc lực, có thể support ở mọi bước trong coding cycle: chia nhỏ vấn đề, viết Unit Test, viết code, viết document, refactor code, ...

Đặc biệt, Github vẫn đang liên tục phát triển, cải tiến Copilot từng ngày để đưa ra những tính năng mới. Biết đâu, một vài năm nữa, thay vì TDD, BDD, chúng ta còn có thêm cả CDD - Copilot Driven Development cũng nên =))

Tuy nhiên, ở hiện tại, mình vẫn giữ quan điểm:

Công nghệ chưa thể hoàn toàn thay thế con người, nhưng những người biết dùng công nghệ có thể thay thế những người còn lại.

Vì thế, nếu được, hãy thử cho Copilot một cơ hội anh em nhé :v

Key takeaways

  • Copilot hỗ trợ tốt cho việc viết Unit Test, đặc biệt là với các simple/ repeated code.
  • Copilot boost productivity (mình thấy tăng khoảng 40% khi viết Unit Test), cho phép chúng ta có nhiều thời gian hơn để focus vào logic problems hơn là typing stuff.
  • Đôi khi, Copilot sẽ gợi ý những đoạn code không liên quan (thỉnh thoảng là cả những đoạn code sai syntax). Vậy nên, đừng quá tin tưởng vào Copilot, hãy guide nó bằng các prompt, recheck lại code, và chịu trách nhiệm với kết quả cuối cùng.

References

Bình luận

Bài viết tương tự

- vừa được xem lúc

テストカバレッジの概念の紹介(C0/C1/C2)

C0/C1/C2カバレッジとは. テストカバレッジがどんなものかは、他の記事を読んでください。. その上で、テストケースの分類―C0,C1,C2について説明します。. 以下のようなコードのテストケースを考えて見ます。.

0 0 266

- vừa được xem lúc

Kiểm thử đơn vị - Tầm quan trọng của nó trong kiểm thử phần mềm là gì?

Kiểm thử đơn vị là gì. .

0 0 64

- vừa được xem lúc

Laravel: Một số phương thức viết Unit Test cho các ca siêu khó

Trong quá trình viết Unit Test cho các dự án chắc hẳn bạn sẽ gặp phải một số ca Unit Test cực khó, hoặc là bạn phải refactor lại khá nhiều, hoặc là phải chuyển qua viết Feature Test. Nhưng không phải lúc nào Feature Test cũng hoạt động, ví dụ như bạn cần viết test cho Jobs hoặc Models chẳng hạn.

0 0 157

- vừa được xem lúc

Chìa khóa kiểm thử đơn vị thành công - Làm thế nào các nhà phát triển kiểm thử mã code của họ?

Người kiểm thử Hộp đen không quan tâm đến kiểm thử đơn vị. Mục tiêu chính của họ là xác thực ứng dụng so với các yêu cầu mà không cần đi sâu vào chi tiết triển khai.

0 0 99

- vừa được xem lúc

Unit test căn bản - Vài khái niệm bước đầu về testing

Hẳn mọi developer đều từng nghe về unit test và những lợi ích nó mang lại. Mình cũng vậy, dạo gần đây mình đã bắt đầu tìm hiểu về unit test và áp dụng cho project của mình.

0 0 172

- vừa được xem lúc

Testing with JUnit in Java

1.Giới thiệu về JUnit.

0 0 99