Computer >> Hướng Dẫn Máy Tính >  >> Hệ Thống >> Android

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test

Tác giả: Siamak Mahmoudi

TDD (Test-Driven Development) là phương pháp phát triển phần mềm nơi test được viết trước khi implement code thực tế.

Nó đòi hỏi hiểu rõ "Cái gì" và "Như thế nào" trong yêu cầu dự án/tính năng.

TDD giúp viết code ít hơn nhưng đủ. Nó giúp ngăn chặn lỗi phổ biến: over-engineering, test coverage quá nhiều, bỏ lỡ yêu cầu chính, function/class quá lớn, câu lệnh code phức tạp quá nhiều.

Tổng thể, nó giúp có codebase sạch, đã cover unit test, ngắn gọn. Theo thời gian, cũng tiết kiệm chi phí dev và maintain code.

Bài này thảo luận TDD thực chiến. Bối cảnh là môi trường dev Android, dùng Kotlin và JUnit5 với dự án mẫu demo các bước. Tuy nhiên, hướng dẫn và kỹ thuật áp dụng được cho ngôn ngữ khác.

Điều kiện tiên quyết

  • Kiến thức cơ bản Kotlin
  • Kiến thức cơ bản viết Unit test
  • Kiến thức về mock và assertion

Dùng Kotlin làm ngôn ngữ và JUnit5 viết unit test. Mockito cho mock và spy. Đối tượng: bất kỳ software developer nào tìm chương mới sự nghiệp. Dù bối cảnh Android, nội dung không nói về tính năng platform-specific. Thay vào đó, tập trung vào kỹ thuật, ghi chú, thách thức khi dev với TDD.

Nếu OK, bắt đầu nào.

Test Driven Development hoạt động như thế nào

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Chu kỳ TDD

Quy trình dev theo chu kỳ:

  1. Viết test thất bại (ô vuông hồng).
  2. Implement code để test pass (ô vuông xanh).
  3. Refactor code (ô vuông xanh lam) nếu cần同时确保 test vẫn pass (ô vuông xanh nhạt).
  4. Viết test thất bại mới (khởi động lại luồng)

Viết test thất bại (Ô vuông hồng)

Bước này, bạn bắt đầu bằng mô tả muốn code làm gì.

Hãy tưởng tượng bạn cho code một bài test kiểm tra nó có hành xử đúng không. Test giống như câu hỏi bạn hỏi code, kiểu "Bạn làm được task này không?"

Ban đầu, code không biết câu trả lời, nên bạn viết test nên fail vì code chưa biết làm task. Test fail giống như dấu hiệu cảnh báo hồng báo hiệu có gì không đúng.

Khi xong giai đoạn, JUnit5 tạo báo cáo toàn diện từ test bạn tạo. Những test này là đại diện hữu hình công việc bạn làm.

Hãy tưởng tượng project manager đọc test case để đánh giá coverage và độ chính xác nắm bắt feature/sản phẩm. Góc nhìn này giúp hiểu rõ ý nghĩa giai đoạn dev này.

Chuyển trọng tâm từ chi tiết kỹ thuật sang hành vi phần mềm.

Thay vì mắc kẹt ở khía cạnh kỹ thuật, hãy chú ý phần mềm hoạt động và tương tác với user/ thành phần khác như thế nào.

Sự chuyển đổi này giúp ưu tiên hành động và kết quả dự định của phần mềm, dẫn đến test phản ánh chính xác hành vi thực tế.

Bằng cách tập trung vào hành vi thay vì chi tiết kỹ thuật, bạn đảm bảo test align chặt chẽ với mục đích và kỳ vọng user.

Trong vài trường hợp, bạn chỉ có vài test case mỗi component (đây là mục đích: ít việc nhưng nhắm đúng) và hoàn toàn ổn, miễn cover tất cả yêu cầu hành vi của dự án.

Mẹo

  • Cụ thể: Viết test case rõ ràng, cụ thể tập trung một khía cạnh hành vi code.
  • Bắt đầu đơn giản: Bắt đầu test case đơn giản nhất cover chức năng cơ bản cần.
@Test fun `a sum is calculated from two input numbers`() {} 
  • Dùng tên có nghĩa: Đặt tên test mô tả để ai đọc biết test kiểm tra gì.
@Test fun `Font Ratio is fetched from data source INITIALLY`() {} 

Lỗi thường gặp cần tránh

  • Test quá nhiều cùng lúc: Tránh test nhiều thứ trong một test. Khó xác định gì đang fail.
// Đừng làm vậy @Test fun `pixelSize fits the standart sizes while fontSize is bigger than minumum supported font size but matches the list of special levels of size`() {} 
  • Phụ thuộc chi tiết implement: Đừng viết test gắn chặt với việc bên trong code. Test tập trung hành vi, không phải implement.
// Đừng làm vậy @Test fun `pixelSize is Long and Non-Null and fits the standart sizes then calculated font size is non-null and of type Dimention`() {} 

Implement code để test pass (Ô vuông xanh)

Giờ đã có test, đến lúc dạy code làm task đúng.

Bạn viết code thực tế khiến test pass và code trả lời đúng câu hỏi.

Khi code pass test, giống như đèn xanh báo "Có, tôi làm được task rồi!".

Bước này về việc đảm bảo code hiểu và giải quyết được vấn đề bạn hỏi.

Mẹo

  • Viết code tối thiểu: Viết code đơn giản nhất khiến test fail pass. Đọc thêm cách tránh over-engineering ở đây.
// Test Case @Test fun `Storage stores font ratio in key-value`() { // Given val fontRatio = 2.0f val mockEditor = mockk<SharedPreferences.Editor>(relaxed = true) every { mockSharedPreference.edit() } returns mockEditor every { mockEditor.putFloat(any(), any()) } returns mockEditor every { mockEditor.apply() } just Runs // When storage.saveFontRatio(fontRatio) // Then verify(exactly = 1) { mockEditor.apply() } } // Implement đúng - Tránh implement thừa class SharedPreferenceHelper( private val sharedPreferences: SharedPreferences ) { fun saveFontRatio(fontRatio: Float) { sharedPreferences.edit().putFloat("font-ratio", fontRatio).apply() } } // Implement sai class SharedPreferenceHelper( private val sharedPreferences: SharedPreferences ) { fun saveFontRatio(fontRatio: Float) { if (fontRatio <= 0.0f) throw IllegalArgumentException("Font ratio must be greater than 0.0f") storeValue(key = FONT_RATIO_KEY, value = fontRatio) } private fun storeValue(key: String, value: Float){ val editor = sharedPreferences.edit() editor.putFloat(key, value) editor.apply() } fun getFontRatio(): Float { return sharedPreferences.getFloat("font_ratio", 1.0f) } } 
  • Tránh trùng lặp: Đừng lặp code. Nếu viết logic tương tự nhiều nơi, cân nhắc refactor. Cải tiến code chính này có thể làm ở giai đoạn này, nhưng nếu sửa đổi có side-effect thì bỏ qua.
class ... { override fun getDefaultFontSize(): Float { val zoomRatio = DEFAULT_SSPEED * DEFAULT_FONT_RATIO / deviceDensity val fontSize = zoomRatio * standardFontSize return fontSize } override fun getFontSizeBySSpeed(speed: Int): Float { val zoomRatio = speed * DEFAULT_FONT_RATIO / deviceDensity val fontSize = zoomRatio * standardFontSize return fontSize } } 
class ... { override fun getDefaultFontSize(): Float = calculate(DEFAULT_AGE) override fun getFontSizeByAge(age: Int): Float = calculate(age) private fun calculate(age: Int): Float { val zoomRatio = age * DEFAULT_FONT_RATIO / deviceDensity val fontSize = zoomRatio * standardFontSize return fontSize } } 

Lỗi thường gặp:

  • Nhảy vọt (Jumping Ahead): Đừng viết code nhiều hơn cần để pass test. TDD khuyến khích phát triển tăng dần. Khi nhảy vọt, bạn giải quyết vấn đề chưa liên quan trực tiếp test hiện tại. Mục tiêu chính: tập trung task ngay trước mắt – pass test hiện tại – không bị phân tâm bởi tính năng tương lai.
  • Bỏ qua test fail: Nếu test không fail ban đầu, có thể bạn bỏ lỡ case quan trọng. Có vẻ khó tin lúc đầu, nhưng sau dev một thời gian trong test component bạn sẽ viết nhiều test cho một method test khía cạnh logic khác. Đây là nơi bạn không nên vui nếu logic chưa implement mà pass test. Nói đơn giản, đây là cách bắt bug ở giai đoạn dev. Vì vậy, hãy mong đợi failure khi nó nên xảy ra.

Refactor code (Ô vuông xanh lam) và đảm bảo test success (Ô vuông xanh nhạt)

Khi code pass test, đến lúc dọn dẹp.

Bạn có thể thấy cách làm code tổ chức hơn, dễ hiểu hơn, hoặc nhanh hơn. Nghĩ như dọn phòng sau chơi, làm mọi thứ gọn gàng sau khi chơi xong. Bạn cải thiện code mà không thay đổi chức năng.

Khi làm vậy, bạn chạy lại tất cả test đảm bảo vẫn pass. Nếu test fail lúc này, giống như dấu hiệu cảnh báo xanh nhạt báo hiệu điều gì bạn dọn dẹp có thể làm hỏng code.

Bạn có thể coi phần này như giai đoạn maintain code riêng.

Giả sử bạn nhận task dọn code cũ và đảm bảo tuân thủ guideline chất lượng code team cũng như yêu cầu sản phẩm.

Trong quá trình này, chìa khóa là duy trì cân bằng tinh tế – tinh chỉnh đồng thời đảm bảo test tiếp tục thành công.

Một số ý tưởng và chiến lược cho giai đoạn refactor:

  • Code clarity: Đơn giản hóa phần phức tạp, thay tên biến không rõ ràng, bổ sung comment để code dễ hiểu cho người khác (và bản thân tương lai).
  • Modularity: Chia function lớn thành function nhỏ tập trung. Code hơn modular, dễ maintain và test.
  • Loại bỏ redundancy: Xác định code trùng lặp và gộp thành function/class tái sử dụng. Loại bỏ lặp lại và đảm bảo nhất quán.
  • Optimization: Xác định vùng cải thiện performance. Chỉ optimize nếu có mục tiêu performance cụ thể và bằng chứng code là bottleneck. Optimization ở đây tránh hao tài nguyên, không phải làm code performant.
  • Consistent formatting: Duy trì code style nhất quán, tuân thủ convention team/dự án.
  • Unused code: Xóa biến, function, import không dùng làm rối codebase.
  • Test improvements: Khác với quan niệm thường thấy về TDD, bạn có thể thêm test khi cần. Mở rộng test suite bằng test case mới cover scenario chưa addressed. Giúp duy trì test coverage toàn diện.
  • Documentation: Nếu mục đích code không rõ từ chính code, cân nhắc thêm/cải thiện doc giải thích intent và usage. Đừng làm thói quen. Đây là giải thích bổ sung cho case quan trọng tránh nhầm lẫn.

Lưu ý code TDD nên self-expressing (tự diễn đạt) và độc lập documentation.

Nhớ, khi refactor, quan trọng chạy lại tất cả test đảm bảo vẫn pass.

Mẹo

  • Giữ test toàn diện: Đảm bảo test cover nhiều scenario bắt side-effect vô tình khi refactor.
  • Refactor dần dần: Thay đổi nhỏ code và chạy test thường xuyên bắt regression sớm.

Lỗi thường gặp:

  • Refactor không có test: Refactor không có test dẫn đến hành vi bất ngờ. Nếu có cơ hội bỏ lỡ logic thì cân nhắc viết test cho nó.
  • Thay đổi code quá lớn: Đôi khi thay đổi nhiều dòng hơn code dev để pass test. Luôn cân nhắc phase refactor riêng biệt an toàn & ít tốn kém hơn.

Viết test thất bại mới (Khởi động lại luồng)

Giờ, bạn nghĩ đến việc tiếp theo muốn code làm.

Bắt đầu bằng viết test mới nên fail vì code chưa biết làm task mới. Giống như cho code thử thách mới.

Sau đó, lặp lại chu kỳ: làm test pass bằng code (ô xanh), dọn dẹp nếu cần (ô xanh lam), tiếp tục test đảm bảo mọi thứ chạy (ô xanh nhạt).

Cách này, bạn luôn tiến về trước và xây dựng code từng bước.

Mẹo

  • Bước tăng dần: Thêm test cho chức năng mới theo增量 nhỏ duy trì đường dev rõ ràng. Thay vì implement tính năng phức tạp một lần, chia nhỏ thành mảnh manageable và tạo test cho từng mảnh. Cách này duy trì đường dev rõ ràng,steady, giúp tập trung, giảm rủi ro, đảm bảo mỗi thêm vào được test kỹ.
  • Feedback loop: Dùng feedback từ viết test fail để hướng dẫn implement. Feedback loop highlight bản chất lặp của TDD. Khi tạo test mới và quan sát fail, bạn có insight quý giá hướng dẫn implement.

Feedback loop hoạt động như sau:

  • Thiết lập kỳ vọng: Khi viết test mới, bạn định nghĩa kỳ vọng code hành xử như thế nào. Làm rõ mục tiêu feature mới.
  • Fail ban đầu: Test fail vì code tương ứng thỏa mãn kỳ vọng còn thiếu hoặc chưa hoàn thiện. Fail ban đầu là phần tự nhiên của TDD.
  • Hướng dẫn implement: Feedback từ test fail chỉ direction code cần viết/sửa. Trở thành roadmap dev, phác thảo chức năng mới trông như nào.
  • Tiến độ tăng dần: Khi implement code cần thiết làm test pass, bạn xây dựng dần functionality mong muốn. Mỗi bước được hướng dẫn bởi feedback từ test fail.
  • Xác minh: Khi implement xong, chạy test lại. Nếu pass, xác minh code mới thỏa mãn kỳ vọng ban đầu.

Feedback loop đảm bảo dev chặt chẽ với mục tiêu phần mềm.

Lỗi thường gặp:

  • Viết test sau implement: Đừng viết test sau khi đã implement feature. TDD là viết test trước. Ngay logic nhỏ thêm trước test code nghĩa là có thể waste resource/bug trong code. Toàn bộ ý nghĩa là không thêm logic trừ khi test suite cần.
  • Bỏ qua test fail: Đừng bỏ qua bước này ngay cả khi bạn nghĩ biết implement feature.

Tại sao không bỏ qua bước test fail ngay cả khi tự tin:

  • Clarity of intent: Viết test fail làm rõ intent feature. Buộc bạn xem xét hành vi và kết quả chính xác trước khi nhảy vào implement.
  • Xác minh giả định: Dù bạn nghĩ hiểu feature, tạo test đảm bảo giả định valid. Hiểu có thể đúng, nhưng test validate nó.
  • Safety Net: Viết test fail thiết lập safety net ngăn regression tương lai. Đóng vai specification feature và bắt side-effect vô tình.
  • Incremental development: TDD khuyến khích dev tăng dần. Mỗi feature mới xây step by step, progression rõ ràng từ fail test đến working implement. Bỏ qua bước làm gián đoạn progression.
  • Documentation: Test fail document hành vi kỳ vọng feature. Doc quý giá cho bạn và team, nhất là khi revisit code tương lai. Luôn nhớ có hệ thống generate report liệt kê tất cả test code cho PM và QA. Những report expose chi tiết bạn spotted trong sản phẩm, Vì vậy, hãy thuyết phục họ bạn hiểu sâu điểm.

Cách phát triển dùng TDD

TDD nhấn mạnh tầm quan trọng viết test tự động để drive thiết kế và dev phần mềm. Dẫn đến code reliable, maintainable, dễ thay đổi theo thời gian.

Nhưng làm sao đưa vào thực hành? Bằng cách thử và quen dần.

Hãy thử TDD khi dev feature mới demo cách bắt đầu dùng thực tế.

Chúng ta sẽ implement tính năng auto setting font size.

Chúng ta có app tin tức và user set auto-scroll-speed cho news feed.

Muốn implement feature điều chỉnh font size màn hình từ scroll speed set ở trang User Profile.

Nếu user set scroll speed 0 đến 1, font size tăng 1.3.

Mọi tăng scroll speed vượt 1 làm tăng font size 1.2.

Feature cho user trải nghiệm tốt hơn đọc tin tức.

Tôi cũng share code khám phá trong GitHub repository này.

Hãy clone và chơi với nó.

Cố gắng theo dõi bước khi chúng ta tiến triển. Giúp nắm kỹ thuật và tư duy TDD thực tế.

Mở Android Studio và tạo dự án mới.

Sơ đồ luồng dữ liệu tính năng Font Size Auto Setting

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test DFD của feature mẫu trong app android

Trên là flowchart data flow cuối cùng nên như nào.

Outline từng component:

Class AutoScrollSettingsUseCase xử lý logic tính toán và lưu FontRatio dựa trên Scroll Speed chọn.

Use case này có dependency trên UserRepository lưu giá trị FontRatio.

Trong UserRepository, có method lưu và lấy FontRatio dùng cơ chế Storage. Khi FontRatio mới gửi tới storage, observable nhận emission với giá trị mới.

Trong UserProfileViewModel, có instance AutoScrollSettingsUseCase được gọi khi user update Scroll Speed. Trigger tính toán lại FontRatio và lưu qua repository.

Sẽ có UI component cần thiết ở User Setting cho user input scroll speed mong muốn. Làm bằng element Android tiêu chuẩn như NumberPicker hoặc custom UI (Chúng ta không bàn phần này).

Đây là breakdown feature đơn giản và mỗi lần làm qua, rõ ràng hơn bước và kết quả cuối. Quan trọng làm cho changes của bạn.‌ ‌

Cách viết Test

Bước đầu luôn tạo class test. Trong case này, ít nhất các test class sau:

  • UserRepositoryTest
  • AutoScrollSettingTest
  • UserSettingsViewModelTest

Tôi thích bắt đầu từ ViewModel.

ViewModel là component kiến trúc Android thoát thay đổi lifecycle (foreground, background, focused). Vì vậy, nơi tốt lưu state.

Tạo file test trong thư mục unitTest của source code theo cùng package path với code feature thực.

TDD thực tế khác dev legacy.

Với TDD dùng IDE boost tạo file và property(fields). Nhưng tạo Test file thủ công! Sau vài lần, khía cạnh IDE này trở nên tiện lợi.

Tạo cấu trúc code (package) rồi tạo test class chuột phải package chọn type Class.

Chọn tên mô tả (có thể follow convention nếu chưa).

Ví dụ: xxx is tested for, với xxx tên component test.

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Dùng IDE Tạo File

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test

Giờ, tạo empty test. Cố gắng broad nhất có thể.

Theo diagram, logic feature không nhiều.

Hai chiến lược chính viết unit test function:

  • AAA
  • Given/When/Then
@Test fun `strategy A`(){ // Arrange // Act // Assert } @Test fun `strategy B`(){ // Given // When // Then } 

Chọn một và follow cho tất cả test.

Khái niệm giống nhau: gom code test dễ đọc và maintain.

Đây là gì tôi có lúc này:

class `UserProfileViewModel is tested for` { // Unimplemented Class val viewModel = UserProfileViewModel() @Test fun Font Ratio is fetched from data source`(){} @Test fun `Scroll Speed update is called so fontSize calculations are triggered`() {} @Test fun `Font Ratio is updated with new emissions from data source`() {} } 

Chạy test!

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Test Fail Do Thiếu Test Target

Fail. Thực ra build fail - không phải test.

Chúc mừng! Chúng ta vừa đạt bước đầu tiên chu kỳ TDD:

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Bước đầu tiên chu kỳ TDD

Vì ViewModel chưa tồn tại, ta có màu đỏ.

Giờ, tạo instance ViewModel,

Vì vậy, dùng IDE tạo class missing hoặc code chưa implement.

Để dialog popup, tôi di chuyển con trỏ phần chưa implement và bấm Option + Return (trên macOS).

Sau đó, follow option:

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test TDD Action: Tạo Target File qua UnitTest file

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Chọn Destination Đúng Cho File Mới

Và giờ re-run test (bước cuối):

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Hiển thị test pass

Có! Pass.

Lưu ý test này body rỗng và test nothing! Đúng và ổn.

Chúng ta nên tiếp tục tạo tất cả test class (vẫn body rỗng) cho mọi component trong DFD diagram - tôi share ở đầu bài.

Điều này thêm giúp hiểu rõ feature trước khi implement.

Cuối cùng, sẽ có khoảng 3-4 test class chứa scenario chung và unit test cover.

Trông như sau:

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Minimal Empty Test Cases

Hãy implement một trong số chúng làm ví dụ.

Nhưng trước đó, chúng ta cần làm việc với data model UI và Domain của feature.

Vì vậy, để move data quanh, hãy tạo data class cần trước.

Quay lại class test ProfileViewModel, có empty unit test function. Implement cái đó.

Lưu ý key là đọc test kỹ và tránh implement thừa cũng như assertion thừa.

Chỉ requirement được implement.

Trong case này, cần stream data kết nối data source đã tạo trước (UserRepository).

Đừng quên: Cần Failing Test trước.

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Implement Inner Body

Chú ý phần chưa implement trong body test function (đánh dấu font đỏ).

Giờ, implement code, rồi refactor làm pass.

Tôi dùng MockK cho mock class/object và Turbine test Flow stream ở đây.

Nếu chưa quen đừng hoảng! Check trang chủ và thử.

Tạo dependency trước và thêm vào ViewModel dùng Named Arguments. Named Argument giúp khi tạo parameter qua IDE introduce proper names qua test code.

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Tạo Missing Parameter Dùng IDE Dialogs

Làm tương tự cho biến FontRatio trong Repository.

Cuối cùng, test code cuối có thể như code dưới:

class `UserProfileViewModel is tested for` { init { Dispatchers.setMain(Dispatchers.Unconfined) } val mockUserRepository = mockk<UserRepository>() @Test fun `Font Ratio is fetched from data source`() = runTest { // Given val expectedRatio = 2.0f every { mockUserRepository.fontRatio } returns flowOf(FontRatioUiModel(expectedRatio)) val viewModel = UserProfileViewModel(userRepository = mockUserRepository) // When viewModel.fontRatio.test { val fromDataSource = expectItem() // Then assertEquals(/* expected = */ expectedRatio, /* actual = */ fromDataSource.fontRatio) } } ... } 

Lưu ý chúng ta không implement phần trong ViewModel hay Repository ở đây.

Chỉ tạo phần missing xóa lỗi từ test body.

Chúng ta sẽ implement chi tiết ở iteration sau.

Chạy test giờ.

Chắc chắn fail, vì chưa implement FontRatio trong ProfileViewModel.

Giờ, refactor ViewModel làm test pass (implement tối thiểu).

Trong case này, chỉ cần connect state flow tới repository. Đã thêm dependency ở iteration trước.

Code cuối:

class UserProfileViewModel( userRepository: UserRepository ) : ViewModel() { val fontRatio: StateFlow<FontRatioUiModel> = userRepository.fontRatio.stateIn( initialValue = FontRatioUiModel(DEFAULT_FONT_RATIO), scope = viewModelScope, started = SharingStarted.Lazily ) companion object { private const val DEFAULT_FONT_RATIO = 1.0f } } 

Chạy test lại, Và Boom! Pass!

Thành thạo Android TDD: Hướng dẫn thực chiến cho Phát triển dựa trên Test Passing Test After Minimal Implementation Of Main Code

Với Unit test này, chúng ta implement phần chính UserProfileViewModel. Chỉ phần cần thiết. Làm tương tự cho test case còn lại.

Đừng đối xử test case như unit test thường (run pass nhanh).

Dành thời gian hiểu requirement kỹ thuật và sản phẩm trước. Sau đó rollout plan và bắt đầu implement. Sau vài lần, tư duy TDD dễ dàng hơn.

Source Code

Source code và repository dự án tại GitHub page của tôi.

Tự do check và hoàn thành bước tiếp theo. Tôi tách iteration nhánh khác nhau để bạn so sánh với implement của mình.

Kết luận

Vì vậy, sau khi đi sâu TDD và khám phá ins and outs, tôi phải nói, nó game-changer!

Tóm tắt:

  • TDD là viết test trước viết code thực tế. Nghe hơi lạ đầu, nhưng trust me, nó works wonders.
  • Quy trình TDD theo chu kỳ đơn giản: viết test fail, implement code làm test pass, refactor nếu cần giữ mọi thứ chạy mượt.
  • Bằng cách nhấn mạnh test tự động, TDD giúp thiết kế và dev phần mềm solid, maintainable, adaptable theo thời gian.

Chúng ta bắt đầu bằng tạo test class và viết empty test function nhưng task của bạn bây giờ là hoàn thành nó (Hoặc jump đến shared repo ngay :) ).

Có vẻ lạ có test không làm gì, nhưng đó là phần của plan.

Tiếp, chúng ta phác thảo plan rõ ràng feature nên làm gì, dựa trên data flow chart. Giúp hiểu requirement trước khi nhảy implement.

Với plan, bắt đầu implement component cần thiết (ViewModel case này), đảm bảo test fail trước. Đúng, fail test là điều tốt trong TDD!

Dần dần, connect mảnh ghép, như setup class UserAutoScrollSettingsUseCase xử lý tính toán font size dựa trên auto-scroll speed (Check project repo).

Chúng ta cũng xử lý UI component, cho user input scroll speed mong muốn, đảm bảo font size điều chỉnh tương ứng (Check project repo).

Suốt quá trình, giữ code clean và simple, tập trung vào gì cần để test pass. Không complexity thừa!

Cuối cùng, feature "Font Size Auto Setting" chạy, test pass với flying colours.

Nhớ, TDD không phải rush test hay code điên cuồng. Là deliberate (có chủ đích) và thoughtful (suy nghĩ kỹ) trong quy trình dev, trả lớn về long run.

Vì vậy, nếu muốn level up dev game, thử TDD! Approach mạnh mẽ làm code solid hơn, giảm bug, làm bạn developer tốt hơn overall.

Chia sẻ này là cách team tôi làm và nó work cho chúng tôi, nhưng không phải perfect solution cho mọi team/cong ty. Bạn cần tìm ra nó có phải của bạn không. Cho tôi biết nếu bạn nghĩ tôi có thể cải thiện solution.

Happy coding! 🚀

Học lập trình miễn phí. Chương trình mở của freeCodeCamp đã giúp hơn 40.000 người có việc làm developer. Bắt đầu ngay