Author: vuongduong7995

  • Firebase dùng trong Flutter (Phần 1):

    1. Tổng quan.

    Firebase là một nền tảng để phát triển ứng dụng di động và trang web, bao gồm các API đơn giản và mạnh mẽ mà không cần backend hay server.

    Lợi ích của Firebase là gì? Firebase còn giúp các lập trình viên rút ngắn thời gian triển khai và mở rộng quy mô của ứng dụng mà họ đang phát triển. Firebase là dịch vụ cơ sở dữ liệu hoạt động trên nền tảng đám mây – cloud.

    Kèm theo đó là hệ thống máy chủ cực kỳ mạnh mẽ của Google. Chức năng chính là giúp người dùng lập trình ứng dụng bằng cách đơn giản hóa các thao tác với cơ sở dữ liệu.

    Cụ thể là những giao diện lập trình ứng dụng API đơn giản. Mục đích nhằm tăng số lượng người dùng và thu lại nhiều lợi nhuận hơn.

    Đặc biệt, còn là dịch vụ đa năng và bảo mật cực tốt. Firebase hỗ trợ cả hai nền tảng Android và IOS. Và Flutter framework cũng hỗ trợ đa nền tảng đặc biệt là phát triển ứng dựng mobile cho cả Android và IOS từ một source code. Vì vậy, tôi muốn trình bày việc sử dụng Firebase trong Flutter.

    2. Firebase Authentication.

    2.1. Thêm Firebase Authentication vào ứng dựng.

    1. Từ root của ứng dụng, chạy đoạn command dưới đây:

    flutter pub add firebase_auth

    • Sau khi hoàn tất, hãy xây dựng lại ứng dụng Flutter của bạn.

    flutter run

    • Gọi thư viện vào trong Dart code:

    import ‘package:firebase_auth/firebase_auth.dart’;

    2.2. Kiểm tra các trạng thái hiện tại.

    Firebase Auth cung cấp nhiều phương pháp và tiện ích cho phép bạn tích hợp xác thực an toàn vào ứng dụng Flutter mới hoặc hiện có của mình. Trong nhiều trường hợp, bạn sẽ cần biết về trạng thái xác thực của người dùng, chẳng hạn như họ đã đăng nhập hay đã đăng xuất.

    Có 3 phương thức để lắng nghe các trạng thái thay đổi xác thực:

    a. authStateChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.

    b. idTokenChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.
    • Khi có sự thay đổi trong mã thông báo của người dùng hiện tại.

    c. userChanges()

    Các sự kiện sẽ kích hoạt khi xảy ra những điều sau:

    • Ngay sau khi người nghe đã được đăng ký.
    • Khi người dùng đã đăng nhập.
    • Khi người dùng hiện tại đã đăng xuất.
    • Khi có sự thay đổi trong mã thông báo của người dùng hiện tại.

    Chú ý: idTokenChanges(), userChanges() & authStateChanges() sẽ không kích hoạt nếu bạn cập nhật Hồ sơ người dùng bằng SDK quản trị Firebase. Bạn sẽ phải buộc tải lại bằng cách sử dụng FirebaseAuth.instance.currentUser.reload() để truy xuất hồ sơ Người dùng mới nhất.

    idTokenChanges(), userChanges() & authStateChanges() cũng sẽ không kích hoạt nếu bạn tắt hoặc xóa Người dùng bằng SDK quản trị Firebase hoặc bảng điều khiển Firebase. Bạn sẽ phải buộc tải lại bằng FirebaseAuth.instance.currentUser.reload(), điều này sẽ gây ra trường hợp người dùng bị vô hiệu hóa hoặc không tìm thấy ngoại lệ mà bạn có thể bắt và xử lý trong code ứng dụng của mình.

    2.3. Quản lý Users trong Firebase.

    a. Tạo tài khoản.

    Sử dụng phương thức createUserWithEmailAndPassword():

    b. Đăng nhập với email.

    Sử dụng phương thức: signInWithEmailAndPassword():

    c. Lấy thông tin user hiện tại.

    d. Lấy thông tin được cung cấp cụ thể của người dùng.

    e. Cập nhật thông tin người dùng.

    Sử dụng phương thức update().

    f. Xóa người dùng hiện tại.

    Sử dụng phương thức delete()

    Trên đây là những kiến thức cơ bản về Firebase ứng dụng trong Flutter, ở phần tiếp theo tôi sẽ trình biết tiếp về ứng Firestore và Sotorage.

  • Stack with Heap Memory Allocation

    1. Stack memory allocation

    Stack memory allocation: Việc cấp phát này xảy ra trên các khối bộ nhớ liền kề. Chúng ta gọi nó là Stack memory allocation vì việc cấp phát xảy ra trong ngăn xếp lệnh gọi hàm. Kích thước của bộ nhớ được cấp phát đã được trình biên dịch biết và bất cứ khi nào một hàm được gọi, các biến của nó sẽ nhận được bộ nhớ được cấp phát trên ngăn xếp. Và bất cứ khi nào gọi hàm kết thúc, bộ nhớ cho các biến sẽ được hủy cấp phát. Tất cả điều này xảy ra bằng cách sử dụng một số quy trình được xác định trước trong trình biên dịch. Lập trình viên không phải lo lắng về việc cấp phát bộ nhớ và hủy cấp phát các biến ngăn xếp. Loại cấp phát bộ nhớ này còn được gọi là Cấp phát bộ nhớ tạm thời bởi vì ngay sau khi phương thức kết thúc việc thực thi, tất cả dữ liệu thuộc về phương thức đó sẽ tự động thoát ra khỏi ngăn xếp. Có nghĩa là, bất kỳ giá trị nào được lưu trữ trong lược đồ bộ nhớ ngăn xếp đều có thể truy cập được miễn là phương thức chưa hoàn thành việc thực thi và hiện ở trạng thái đang chạy.

    Những điểm chính của bộ nhớ stack:

    1. Nó cấp phát bộ nhớ tạm thời trong đó các thành viên dữ liệu chỉ có thể truy cập được nếu phương thức chứa chúng hiện đang chạy.
    2. Nó tự động cấp phát hoặc hủy cấp phát bộ nhớ ngay sau khi phương thức tương ứng hoàn thành việc thực thi.
    3. Chúng ta nhận được nếu bộ nhớ ngăn xếp được lấp đầy hoàn toàn.
    4. Cấp phát bộ nhớ stack được coi là an toàn hơn so với cấp phát bộ nhớ heap vì dữ liệu được lưu trữ chỉ có thể được truy cập bởi luồng chủ của nó.
    5. Cấp phát và thu hồi cấp phát bộ nhớ nhanh hơn so với cấp phát bộ nhớ Heap.
    6. Bộ nhớ stack được cấp phát bộ nhớ lưu trữ nhỏ hơn so với bộ nhớ Heap.

    Tất cả các biện trên sẽ được cấp pháp bộ nhớ trong bộ nhớ stack.

    2. Heap memory allocation

    Bộ nhớ được cấp phát trong quá trình thực thi các lệnh do người lập trình viết. Lưu ý rằng tên heap không liên quan gì đến cấu trúc dữ liệu heap. Nó được gọi là heap vì nó là một không gian bộ nhớ có sẵn cho các lập trình viên để cấp phát và thu hồi cấp phát. Mỗi khi chúng ta tạo một đối tượng, nó luôn tạo ra trong Heap-space và thông tin tham chiếu đến các đối tượng này luôn được lưu trữ trong bộ nhớ stack. Phân bổ bộ nhớ Heap không an toàn như phân bổ bộ nhớ Stack vì dữ liệu được lưu trữ trong vùng này có thể truy cập hoặc hiển thị cho tất cả các chuỗi. Nếu một lập trình viên không xử lý tốt bộ nhớ này, thì chương trình có thể bị thiếu bộ nhớ.

    Việc cấp phát bộ nhớ Heap được chia thành ba loại. Ba loại này giúp chúng ta sắp xếp thứ tự ưu tiên dữ liệu (đối tượng) sẽ được lưu trữ trong bộ nhớ Heap hoặc trong quá trình thu gom rác (quá trình xác định và loại bỏ các Object không được sử dụng (unreferenced) khỏi bộ nhớ Heap) bao gồm:

    1. Young Generation: Phần bộ nhớ nơi tất cả dữ liệu (đối tượng) mới được tạo ra để phân bổ vùng và bất cứ khi nào bộ nhớ này được lấp đầy hoàn toàn thì phần còn lại của dữ liệu sẽ được lưu trữ trong bộ sưu tập Rác.
    2. Old or Tenured Generation: Phần của bộ nhớ Heap chứa các đối tượng dữ liệu cũ hơn không được sử dụng thường xuyên hoặc không được sử dụng nữa sẽ được đặt.
    3. Permanent Generation: Đây là phần của bộ nhớ Heap chứa siêu dữ liệu của JVM cho các lớp thời gian chạy và các phương thức ứng dụng.

    Những điểm chính của hepa memory allocation:

    1. Chúng ta nhận được thông báo lỗi tương ứng nếu Heap-space đã đầy hoàn toàn.
    2. Việc cấp phát bộ nhớ này khác với bộ nhớ stack, ở đây không cung cấp tính năng tự động thu hồi bộ nhớ. Chúng ta cần sử dụng quá trình xác định và loại bỏ rác để loại bỏ các đối tượng cũ không sử dụng để sử dụng bộ nhớ một cách hiệu quả.
    3. Thời gian xử lý (Thời gian truy cập) của bộ nhớ này khá chậm so với bộ nhớ stack.
    4. Bộ nhớ Heap cũng không phải là một luồng an toàn như bộ nhớ stack vì dữ liệu được lưu trữ trong bộ nhớ Heap được hiển thị cho tất cả các luồng.
    5. Bộ nhớ đống có thể truy cập hoặc tồn tại miễn là toàn bộ ứng dụng chạy.
    6. bộ nhớ Heap được cấp phát bộ nhớ lưu trữ lớn hơn so với bộ nhớ stack.

    Bộ nhớ này cho 10 kiểu dữ liệu interger được cấp phát trong Heap.

    Ví dụ:

    Trong ví dụ trên:

    • Khi chúng ta bắt đầu thực thi chương trình có, tất cả các lớp thời gian chạy được lưu trữ trong không gian bộ nhớ Heap.
    • Sau đó, chúng ta tìm thấy phương thức main() trong dòng tiếp theo được lưu trữ trong bộ nhớ stack cùng với tất cả các phương thức nguyên thủy (hoặc cục bộ) và biến tham chiếu Emp kiểu Emp_detail cũng sẽ được lưu trữ trong bộ nhớ stack và sẽ trỏ đến đối tượng tương ứng được lưu trữ trong bộ nhớ Heap.
    • Sau đó, dòng tiếp theo sẽ gọi đến phương thức khởi tạo tham số Emp(int, String) từ main() và nó cũng sẽ cấp phát cho phần trên cùng của cùng một khối bộ nhớ Stack. Điều này sẽ lưu trữ:
      • Đối tượng tham chiếu của đối tượng được gọi của bộ nhớ Stack.
      • Giá trị nguyên thủy (kiểu dữ liệu nguyên thủy) int id trong bộ nhớ Stack.
      • Biến tham chiếu của đối số String emp_name sẽ trỏ đến chuỗi thực từ nhóm chuỗi vào bộ nhớ heap.
    • Sau đó, phương thức main sẽ lại gọi đến phương thức static Emp_detail(), phương thức này sẽ được thực hiện trong khối bộ nhớ Stack trên đầu khối bộ nhớ trước đó.
    • Vì vậy, đối với đối tượng mới tạo Emp kiểu Emp_detail và tất cả các biến thể hiện sẽ được lưu trữ trong bộ nhớ heap.

    Cụ thể như hình dưới:

  • Dart – extends Vs with Vs implements

    Mở đầu:

    Tất cả mọi người khi làm việc với Dart để phát triển ứng dụng bằng cách sử dụng Flutter framework thường xuyên gặp phải các cách sử dụng khác nhau của cách từ khóa: implements, extends và with. Trong Dart, một lớp có thể kế thừa một lớp khác, tức là Dart có thể tạo một lớp mới từ một lớp hiện có. Chúng ta sử dụng các từ khóa để làm như vậy. Trong bài viết này, chúng ta sẽ xem xét 3 trong số các từ khóa được sử dụng cho cùng một mục đích và so sánh chúng, đó là:

    • extends
    • with
    • implements

    1. “extends” keyword.

    Trong Dart, từ khóa “extends” thường được sử dụng để thay đổi hành vi của super class bằng cách sử dụng Inheritance. Một lớp mới sử dụng được các thuộc tính và đặc điểm của lớp hiện có khác được gọi là Tính kế thừa. Nói một cách đơn giản hơn, chúng ta có thể nói rằng, chúng ta sử dụng key word extends để tạo Child classsuper để chỉ Parent Class. Class có các thuộc tính được kế thừa bởi child class được gọi là Parent Class. Parent Class còn được gọi là base class or super class. Class kế thừa các thuộc tính từ lớp khác được gọi là child class. Child class còn được gọi là derived class, heir class, or subclass. “extends” keyword là tính kế thừa của OOP điển hình. Ngoài ra, chúng ta có thể ghi đè các phương thức.

    Chúng ta sử dụng “extends” keyword nếu bạn muốn tạo một phiên bản cụ thể hơn của một lớp. Ví dụ: nếu class Apple extends từ lớp Fruit, điều đó có nghĩa là tất cả các thuộc tính, biến và hàm được định nghĩa trong class Fruit sẽ có sẵn trong class Apple.

    Ví dụ:

    Output:

    2. “implements’ keyword

                Interfaces định nghĩa thiết lập của các phương thức trên một đối tượng. Dart không có cú pháp miêu tả interfaces.  Mọi class được ngầm định nghĩa là một interfaces chưa tất cả các instance members của class và bất kỳ interfaces nào nó thể hiện. Nếu bạn muốn tạo class A hỗ trợ API của class B mà không kế thừa B, class A nên “implements” interface B. Chúng ta sử dụng keyword “implements” để làm điều này. Đặc biệt, để sử dụng tính trừu tượng toàn phần trong dart, chúng ta sử dụng “abstract” phía trước class và sẽ không thể khởi tạo được nó.

    Ví dụ 1:

    Output:

    Ví dụ 2:

    Kết quả:

    3. “with” keyword

                Mixins là con đường tái sử dụng các phương thức của các class. Mixins được hiểu như abstract class để tái sử dụng trong nhiều class có chức năng và thuộc tính tương tự. Mixins là con đường để trừu tượng và tái sử dụng các phép toán và trạng thái. Nó tương tự việc tái sử dụng chúng ta là để mở rộng class nhưng không có đa kế thừa. Vẫn chỉ tồn tại một superClass.

                “with” keyword được sử dụng để bao gồm Mixins. Mixin là một kiểu cấu trúc khác, chỉ được sử dụng với “with” keyword.

                Trong Dart, một lớp có thể giữ vai trò như mixin nếu class đó không có constructor. Điều quan trọng cần lưu ý là mixin không bắt buộc hạn chế kiểu cũng như không áp đặt hạn chế sử dụng đối với các phương thức của class.

    Ví dụ:

    Kết quả:

    4. Tổng kết.

                Trên đây là một số chia sẻ về sự khác nhau của extends, implemens, và with keywords trong Dart nhằm giúp mọi người sử dụng đúng mục đích. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó.

    Cảm ơn các bạn đã đọc bài viết của mình.

    Author: DuongVT19

  • Concurrency in Dart

    1. Tổng quan.

                Dart hỗ trợ chương trình chạy đồng thời với async-await, isolates and các class như Future và Stream.

                Với một ứng dựng, tất cả code của Dart chạy trong main isolate. Mỗi isolate có một thread và sẽ không chia sẻ các đối tương có thể biến đổi với các isolates khác. Các isolate truyền đạt thông tin với nhau thông qua message.

                Nhiều Dart app chỉ sử dụng main isolate, nhưng chúng ta có thể tạo thêm các isolate cho phép thực thi đồng thời nhiều event trên nhiều lõi bộ xử lý.

    2. Các loại và cú pháp của bất đồng bộ.

    2.1. Future and Stream types

                Ngôn ngữ và các thư viện Dart sử dụng các đối tượng Future và Stream để các giá trị tương ứng sẽ được cung cấp trong tương lai. Ví dụ, tương ứng với giá trị kiểu int là Future<int> và Stream<int>.

                Tại sao quan trọng phương thức là đồng bộ hay bất đồng bộ? Điều này quan trọng bởi vì hầu hết các app sẽ cần thực hiện nhiều công việc trong cùng một thời gian. Ví dụ: một ứng dụng có thể bắt đầu một yêu cầu HTTP, nhưng cần cập nhật màn hình của ứng dụng đó hoặc phản hồi thông tin đầu vào của người dùng trước khi yêu cầu HTTP hoàn tất. Phương thức bất đông bộ sẽ giúp ứng dụng luôn phản hồi.

    2.2. Async-await

                Async-await cung cấp một các khai báo để xác định các hàm không đồng bộ và sử dụng kết quả của chúng.

    Trong ví dụ trên, hàm main () sử dụng từ khóa await phía trước _readFileAsync() để cho phép mã Dart khác (chẳng hạn như trình xử lý sự kiện) sử dụng CPU trong khi native code (file I/O ) thực thi. Việc sử dụng await cũng có tác dụng chuyển đổi Future < String > được trả về bởi _readFileAsync() thành String. Kết quả là, biến nội dung có kiểu ngầm định là String.

    3. Cách các isolate hoat động.

    Bằng cách sử dụng các isolate, code Dart có thể thực hiện nhiều tác vụ độc lập cùng một lúc, sử dụng các lõi xử lý bổ sung nếu chúng có sẵn. Các isolate giống như các luồng hoặc quy trình, nhưng mỗi isolate có bộ nhớ riêng, một luồng duy nhất chạy một vòng lặp.

    3.1. Main isolate

    Một ứng dụng Dart điển hình thực thi tất cả mã của nó trong main isolate của ứng dụng, như thể hiện trong hình sau:

    Ngay cả các chương trình single-isolate cũng có thể thực thi trơn tru bằng cách sử dụng async-await để đợi các hoạt động không đồng bộ hoàn tất trước khi tiếp tục sang công việc tiếp theo. Một ứng dụng hoạt động tốt sẽ bắt đầu nhanh chóng, truy cập vào vòng lặp sự kiện càng sớm càng tốt. Sau đó, ứng dụng sẽ phản hồi nhanh chóng với từng sự kiện được xếp hàng đợi, sử dụng các hoạt động không đồng bộ nếu cần.

    3.2. Vòng đời của isolate

    Như hình trên cho thấy, mọi isolate bắt đầu bằng cách chạy một số code Dart, chẳng hạn như hàm main(). Code Dart này có thể đăng ký một số trình xử lý sự kiện – để phản hồi thông tin đầu vào của người dùng hoặc file I/O như hình. Khi function khởi tạo của isolate quay trả lại, isolate vẫn tồn tại nếu nó cần xử lý các sự kiện tiếp theo. Sau khi xử lý các sự kiện, isolate sẽ thoát ra.

    3.3. Xử lý sự kiện.

    Trong ứng dụng, hàng đợi sự kiện của main isolate có thể chứa các yêu cầu repaint và các thông báo của tap và các sự kiện giao diện người dùng khác. Ví dụ: hình sau cho thấy một sự kiện repaint, tiếp theo là tap event, tiếp theo là hai sự kiện repaint. Vòng lặp sự kiện nhận các sự kiện từ hàng đợi theo thứ tự xuất trước, nhập trước.

    Nếu một hoạt động đồng bộ mất quá nhiều thời gian xử lý, ứng dụng có thể không phản hồi. Trong hình dưới, sự kiện tap mất quá nhiều thời gian, vì vậy các sự kiện tiếp theo được xử lý quá muộn. Ứng dụng có vẻ như bị đóng băng và bất kỳ hoạt ảnh nào mà nó thực hiện có thể bị giật (jerky). Tệ hơn, giao diện người dùng có thể trở nên hoàn toàn không phản hồi.

    3.4. Background workers

                Nếu giao diện người dùng của ứng dụng của bạn không phản hồi do tính toán tốn nhiều thời gian như: parsing a large JSON file, hãy xem xét tách ra sử dụng worker isolate, thường được gọi là background worker. Một trường hợp phổ biến, được hiển thị trong hình sau, là sinh ra worker isolate đơn thực hiện tính toán và sau đó thoát ra. Worker isolate trả về kết quả của nó trong một thông báo khi isolate thoát ra.

    Mỗi isolate message có thể gửi một đối tượng, bao gồm bất kỳ thứ gì có thể truy cập chuyển tiếp từ đối tượng đó. Không phải tất cả các loại đối tượng đều có thể gửi được và việc gửi không thành công nếu bất kỳ đối tượng có thể truy cập chuyển tiếp nào không thể gửi được. Ví dụ: bạn chỉ có thể gửi một đối tượng kiểu List<Object> nếu không có đối tượng nào trong danh sách là không thể điều chỉnh được. Nếu một trong các đối tượng là Socket, thì quá trình gửi không thành công vì các socket không thể gửi được.

    Note: Trong Flutter chúng ta có thể sử dụng function compute() để lấy giá trị trong tương lai tương ứng, tuy nhiên cần lưu ý là  đối số gọi lại phải là một hàm cấp cao nhất, không phải là một closure hoặc một instance hoặc phương thức tĩnh của một lớp.

    4. Ví dụ:

    Hàm _parseInBackground() chứa code sinh ra (tạo và bắt đầu) vùng worker isolate, sau đó trả về kết quả:

    1. Trước khi tạo ra worker isolate, khởi tạo ra một cổng nhận (Receiver), cho phép worker isolate gửi tin nhắn đến main isolate.
    2. Tiếp theo là lệnh gọi Isolate.spawn(), tạo và bắt đầu isolate cho background worker. Đối số đầu tiên của Isolate.spawn() là hàm mà worker isolate thực thi: _readAndParseJson. Đối số thứ hai là SendPort mà worker isolate có thể sử dụng để gửi tin nhắn đến main isolate. Không khởi tạo SendPort; nó sử dụng thuộc tính sendPort của ReceivePort.
    3. Sau khi isolate được sinh sản, main isolate sẽ đợi kết quả. Bởi vì lớp GetPort triển khai Stream, thuộc tính đầu tiên là con đường dễ dàng để lấy một giá từ worker isolate.

    Code:

    Mô hình làm việc của ví dụ trên:

    5. Tổng kết.

    Trên đây là một số chia sẻ về việc sử dụng Concurrency trong Dart nhằm tăng hiệu suất cho ứng dựng. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó.

    Cảm ơn các bạn đã đọc bài viết của mình.

    Author: DuongVT19

  • Flutter Unit test: Mock dependencies using Mockito

    1. Tại sao phải sử dụng Mockito.

    Các unit tests có thể sẽ phụ thuộc vào các class có fetch data từ web và cơ sở dữ liệu. Điều này bất tiền do một vài lý do:

    • Việc fetch data từ web và cơ sở dữ liệu về sẽ chậm để thực hiện kiểm tra.
    • Việc kiểm tra có thể bắt đầu lỗi nếu kết quả từ web và cơ sở dữ liệu là kết quả không được mong đợi. Điều này gọi là “flaky test”.
    • Khó để kiểm tra tất cả các trường hợp thành công hay lỗi các hành động sử dụng web và cơ sở dữ liệu.

    Vì vậy, chúng ta có thể sử dụng “mock” dependencies. Mocks cho phép mô phỏng web và cơ sở dữ liệu và trả về kết quả cụ thể tùy thuộc vào tình huống.

    2. Các bước sử dụng Mockito package để kiểm tra.

    Các bước bao gồm:

    1. Thêm các package vào pubspec.yaml.
    2. Tạo function để kiểm tra.
    3. Tạo file test với mock http.Client.
    4. Viết các test cho mỗi điều kiện.
    5. Chạy kiểm tra.

    2.1. Thêm các package dependencies vào pubspec.yaml.

    Thêm các package dependencies: http, mockito, build_runner với version mới nhất ( tham khảo trên trang pub.dev) vào file pubspec.yaml.

    2.2. Tạo function để kiểm tra.

    Ở ví dụ này, chúng ta tạo một unit test fetchAlbum function để lấy dữ liệu từ internet. Để kiểm tra function này, chúng ta cần 2 thay đổi đó là:

    1. Tham số của function phải có http.Client. Điều này cho phép cung cấp chính xác http.Client phụ thuộc trong trường hợp. Đối với Flutter và dự án web và cơ sở dữ liệu-side cung cấp http.IOClient. Đối với Browse apps, cung cấp http.BrowserClient. Đối với tests, cung cấp mock http.Client.
    2. Sử dụng client để lấy dữ liệu từ internet điều này tốt hơn là phương pháp http.get() trong việc làm giả.

    2.3. Tạo file test với mock http.Client.

    Tạo file fetch_album_test.dart trong folder test.

    Thêm annotation @GenerateMocks([http.Client]) để main function tạo ra MockClient class với mockito. MockClient class được tạo ra sẽ thể hiện http.Client class. Điều này cho phép chúng ta có thể thực hiện fetchAlbum function và trả lại các kết quả khác nhau cho mỗi trường hợp test trong MockClient.

    Các mocks được sinh ra sẽ được để trong .

    Sau đó, trong command chạy: flutter pub run build_runner build để sinh ra mocks.

    2.4. Viết các test cho mỗi điều kiện.

    Ở ví dụ này, fetchAlbum() function sẽ trả về:

    1. Nếu http gọi thành công sẽ trả về Album()
    2. Nếu lỗi thì sẽ trả về Exception.

    2.5. Chạy file tests.

    Chúng ta chỉ việc ấn Run trong file tests trong thư mục test: …/test/fetch_album_test.dart.

    3. Tổng kết

    Trên đây là một số chia sẻ về việc sử dụng Unit test: Mock dependencies using Mockito cơ bản giúp cho việc kiểm tra các function thao tác với web và cơ sở dữ liệu dễ dàng hơn. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó.

    Cảm ơn các bạn đã đọc bài viết của mình.

    Source code: https://github.com/falcon12795/unit_test

    Tài liệu tham khảo: https://docs.flutter.dev/cookbook/testing/unit/mocking

    Author: DuongVT19