Mã sạch Kotlin cho Android, Phần 1


Ngô Bảo Quyên
8 tháng trước
Hữu ích 5 Chia sẻ Viết bình luận 0
Đã xem 4154

Thử nghiệm phát triển dựa trên Android bằng cách sử dụng Kotlin

Bài đăng này nói về hành trình tìm kiếm khung thử nghiệm đơn vị ứng dụng di động phù hợp của tôi và cách tôi đến với Mã sạch Kotlin cho Android. Một mẫu thiết kế, nó là một bản chuyển thể của Android Clean Code . Khi bạn hoàn thành loạt blog này , tôi hứa, bạn sẽ học cách kiểm tra đơn vị ứng dụng di động của mình, từng mảnh một.

Hãy để chúng tôi bắt đầu với thử nghiệm đơn vị

Viết một ứng dụng Android có độ bao phủ mã kiểm tra đơn vị tốt là không dễ, vì mã Android thường có một lớp hoạt động hoặc lớp phân mảnh lớn quản lý nhiều hơn một chức năng hoặc nhiệm vụ. Các lớp phân đoạn điển hình thực hiện các nhiệm vụ dưới đây:

  • Truy xuất dữ liệu từ các nguồn dữ liệu như API, cơ sở dữ liệu cục bộ hoặc nhà cung cấp nội dung.
  • Thao tác dữ liệu và trang trí dữ liệu được truy xuất theo nhu cầu UI.
  • Các hoạt động cụ thể của UI như kết xuất, vẽ, phản hồi các sự kiện của người dùng và tạo các đoạn.

Khi bạn cố gắng hoàn thành nhiều hơn một nhiệm vụ trong một lớp, thường thì Activity hoặc Fragment sẽ trở thành một lớp lớn và các phương thức của nó được đan xen và làm mọi thứ, ở mọi nơi. Kiểm tra các phương thức trong thiết lập này là không dễ dàng, bạn cần phải có quá nhiều phụ thuộc cần phải chế giễu. Việc duy trì mã kiểm tra với nhiều giả lập tốn rất nhiều công sức. Khi xem xét các công cụ kiểm tra đơn vị như JUnit, Robolectric và thiết bị Android, chúng ta thường bỏ qua khả năng thích ứng kiến ​​trúc phát triển ứng dụng để kiểm tra đơn vị.

Một trong những lý do chính đằng sau sự thất bại của tự động hóa thử nghiệm đơn vị là một kiến ​​trúc của ứng dụng không hỗ trợ thử nghiệm đơn vị.

Điều quan trọng là phải có một kiến ​​trúc phát triển hỗ trợ thử nghiệm và sử dụng chế độ chế tạo tối thiểu hoặc không chế nhạo chút nào. Khi kiến ​​trúc phát triển ứng dụng của bạn không hỗ trợ thử nghiệm đơn vị vốn có, khung thử nghiệm không thể nâng bạn khỏi món súp bạn đã tạo cho chính mình.

Biết rằng chúng ta cần một mẫu thiết kế tốt hỗ trợ thử nghiệm và sử dụng kiến trúc sạch của chú Bob làm hướng dẫn tham khảo, tôi đã bắt đầu tìm kiếm các mẫu thiết kế phù hợp để phát triển dựa trên thử nghiệm trong các ứng dụng di động.

Các mô hình phát triển di động bản địa

Trong số rất nhiều mẫu thiết kế được tạo ra để phát triển ứng dụng di động, một mẫu gần giống với kiến ​​trúc mã sạch và phổ biến trong thế giới iOS là VIPER .

Việc triển khai vanilla đơn giản của VIPER trong iOS có những thách thức riêng. Không có cấu hình nào trong Kiến trúc sạch hoặc VIPER của chú Bob (thực hiện tất cả các thiết lập trong đại biểu ứng dụng) và dẫn đến mã bị vấy bẩn với mã thiết lập không liên quan. Clean-swift là một mẫu iOS khắc phục những thách thức này, trong đó hệ thống dây của các phụ thuộc được trích xuất đến cấu hình.

Nếu các từ VIPER và bộ cấu hình nghe có vẻ mới đối với bạn, đừng lo lắng, hãy tiếp tục đọc, bạn sẽ hiểu nó sau.

Đợi một chút, chúng ta đang thảo luận về các mẫu thiết kế Android và đề xuất là sử dụng một mẫu thiết kế iOS?

Tại sao không?

Sự phát triển của Android và iOS rất giống nhau và với mẫu thiết kế sạch sẽ, chúng tôi sẽ có thể kiểm tra ít nhất 80% mã.

Buộc VIPER vào Android có nhiều vấn đề, bạn có thể đọc thêm về nó trên blog Lyubomir . Trong Android Clean Code, được điều chỉnh từ Clean-swift, các vấn đề được đề cập trong blog Lyubomir đã được quan tâm.

Mã nguồn sạch dành cho Android

Chào mừng bạn đến với thế giới này, Kotlin CleanCode cho Android, được xây dựng từ Clean-swift. Nó được xây dựng trên nguyên tắc  lập trình RẮN .

Mẫu này là một phiên bản ngẫu hứng của Mã sạch Android được tạo cho Java. Muốn xem nó hoạt động như thế nào với Java - kiểm tra ở đây

Kotlin CleanCode4Android được xây dựng bằng cách chia hoạt động nguyên khối thành nhiều đơn vị.

A) Mỗi ​​phân đoạn hoặc hoạt động cần lấy dữ liệu từ một hoặc nhiều nguồn dữ liệu, công việc truy xuất dữ liệu sẽ được xử lý bởi một lớp tương tác.

B) Sau khi dữ liệu được bộ tương tác truy xuất, dữ liệu có thể cần phải được sửa đổi ở dạng có thể được trình bày bởi đoạn, lớp thực hiện công việc này là người trình bày.

C) Fragment thực hiện công việc thường xuyên là trình bày dữ liệu và chấp nhận hành động của người dùng dưới dạng sự kiện.

Làm thế nào để các mảnh, người tương tác và người thuyết trình nói chuyện với nhau?

Sử dụng các giao diện.

Khi màn hình cần được tải, nó gọi Interactor bằng giao diện InteractorInput để lấy dữ liệu.

Khi truy xuất dữ liệu thành công hoặc thất bại, Interactor gọi Presenter bằng giao diện PresentorInput để trang trí dữ liệu thô.

Khi dữ liệu ở dạng có thể trình bày, Người trình bày gọi Fragment bằng giao diện FragmentInput để trình bày dữ liệu. Như bạn thấy luồng dữ liệu là một chiều .

Tại sao nên sử dụng giao diện?

Các lớp nói chuyện với nhau bằng các giao diện này, do đó, trong quá trình kiểm tra đơn vị, chúng tôi sẽ có thể chế giễu các lớp khác mà không phải đổ mồ hôi nhiều.

Băt đâu nao

Sao chép dự án ví dụ và mở nó trong Android Studio.

Miếng

Khi thiết kế lớp phân mảnh, chúng ta phải rõ ràng lớp phân mảnh nên làm gì và không nên làm gì.

Giữ nguyên tắc SRP, công việc của đoạn là trình bày dữ liệu cho người dùng và lắng nghe hành động của người dùng, không hơn, không kém.

Đối với đoạn để trình bày dữ liệu, nó cần dữ liệu từ các nguồn dữ liệu. Nó có nên tự lấy dữ liệu không? Không, cần ủy thác công việc cho Interactor bằng cách sử dụng các giao diện. Hãy để chúng tôi xác định giao diện:

interface HomeInteractorInput {
    fun fetchHomeData(request: HomeRequest)
}

Chúng tôi định nghĩa một thành viên trong  Fragment lớp sẽ giữ phiên bản triển khai của giao diện. Các  onCreate phương pháp của Fragment nên gọi phương thức đó sẽ lấy dữ liệu cho hoạt động.

class HomeFragment : Fragment(), HomeFragmentInput {
    lateinit var output: HomeInteractorInput

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        val view =  inflater.inflate(R.layout.fragment_home, container, false)
        HomeConfigurator.configureFragment(this)
        fetchData()
        return view
    }

    fun fetchData() {
        // create Request and set the needed input
        val homeRequest = HomeRequest()
        homeRequest.isFutureTrips = true
        // Call the output to fetch the data
        output.fetchHomeData(homeRequest)
    }
}

Đợi một chút, thành viên đầu ra sẽ được khởi tạo như thế nào với việc thực hiện đúng?

Bộ cấu hình thực hiện nối dây của các phụ thuộc; nhiều hơn về điều đó sau.

Lo lắng về việc viết quá nhiều mã soạn sẵn? Nếu bạn sử dụng công cụ giàn giáo KotlinCleanCode4Android , các lớp này sẽ được tự động tạo cho bạn.

Chúng ta hãy tập trung vào thử nghiệm và viết một trường hợp thử nghiệm để kiểm tra Fragment; bạn có thể tham khảo mã ở đây . Chúng tôi sẽ sử dụng Robolectric thay vì JUnit để kiểm tra đoạn này, vì Robolectric thực hiện rất nhiều việc nhạo báng hệ sinh thái Android như bối cảnh, nhật ký Android, v.v.

Chúng tôi sẽ viết một trường hợp thử nghiệm đơn giản để xem Fragment được tạo ra. Tôi gọi đây là một thử nghiệm Canary, để xem các mảnh cơ bản đã được nối dây đúng cách chưa.

@Test
    fun fragment_ShouldNOT_be_Null() {
        // Given
        val fragment = HomeFragment()
        // When

        // Then
        Assert.assertNotNull(fragment)
    }

Bây giờ chúng ta đã biết đoạn thực sự được tạo ra, chúng ta hãy kiểm tra xem phương thức Interactor có được gọi hay không và xem làm thế nào chúng ta có thể kiểm tra đoạn mà không cần Interactor.

Bạn sẽ có thể kiểm tra mà không thực sự sử dụng Interactor. Hãy để chúng tôi sử dụng một gián điệp để kiểm tra nếu phương thức được gọi.

Muốn hiểu các ý tưởng đằng sau các gián điệp, giả, và sơ khai, hãy kiểm tra ở đây .

Hãy để chúng tôi tạo ra các gián điệp cho giao diện  HomeInteractorInput.

class HomeFragmentOutputSpy : HomeInteractorInput {

        var fetchHomeDataIsCalled = false
        lateinit var homeRequestCopy: HomeRequest

        override fun fetchHomeData(request: HomeRequest) {
            fetchHomeDataIsCalled = true
            homeRequestCopy = request
        }
}

Mã này là tự giải thích, nó thực hiện giao diện và các phương thức cần thiết của giao diện. Ngoài ra, là một gián điệp, nó ghi lại đầu vào và thiết lập thành viên để ghi lại phương thức mà nó đã gọi.

Hãy để chúng tôi tạo phương thức kiểm tra để kiểm tra Interactor được gọi.

@Test
    fun onCreateView_shouldCall_fetchHomeData() {
        // Given
        val fragmentOutputSpy = HomeFragmentOutputSpy()

        // It must have called the onCreateView earlier,
        // we are injecting the mock and calling the fetchData to test our condition
        val homeFragment = HomeFragment()
        homeFragment.output = fragmentOutputSpy

        // When
        homeFragment.fetchData()

        // Then
        Assert.assertTrue(fragmentOutputSpy.fetchHomeDataIsCalled)
    }

Chúng ta cũng kiểm tra xem phương thức tương tác được gọi và đã được cung cấp đúng đầu vào.

@Test
    fun onCreateView_Calls_fetchHomeMetaData_withCorrectData() {
        // Given
        val fragmentOutputSpy = HomeFragmentOutputSpy()
        val homeFragment = HomeFragment()
        homeFragment.output = fragmentOutputSpy

        // When
        homeFragment.fetchData()

        // Then
        Assert.assertNotNull(homeFragment)
        Assert.assertTrue(fragmentOutputSpy.homeRequestCopy.isFutureTrips)
    }

Mặc dù chúng tôi đang kiểm tra đơn vị một lớp có logic đơn giản, bạn có thể hiểu mẫu thiết kế này mạnh đến mức nào, cho phép nhà phát triển kiểm tra xem phương thức có được gọi hay không và xem các giá trị của các tham số được truyền. Trong thực tế, nhà phát triển có thể đơn vị kiểm tra lớp phân mảnh, từng mảnh một.

Trước khi chúng tôi đóng bài tập này, hãy chạy các trường hợp thử nghiệm với phạm vi bảo hiểm mã (Android Studio → Menu → Chạy → Chạy với phạm vi bảo hiểm) và tự mình xem bao nhiêu lớp Fragment được bảo hiểm.

Trong bài tiếp theo, chúng tôi sẽ đề cập đến Interactor. 

Lời cảm ơn

Logo của Suresh CR

Bài viết lấy cảm hứng từ Clean Swift

Hữu ích 5 Chia sẻ Viết bình luận 0
Đã xem 4154