'How to test private functions/methods in Flutter?

I'm currently developing an app that uses the bloc architecture. My bloc is using streams exclusively to communicate with the UI. Therefore all its methods except for the constructor are private ( they start by '_').

So the question is how can I test the bloc's private methods from the test class that lives in the text package so it cannot access private methods of other packages.

Thanks



Solution 1:[1]

You can't, but you can make them public and annotate it with @visibleForTesting to get an DartAnalyzer warning when they are accessed from code that is not in in the same library or in test/

https://github.com/dart-lang/sdk/blob/master/pkg/meta/lib/meta.dart#L224-L233

/// Used to annotate a declaration was made public, so that it is more visible
/// than otherwise necessary, to make code testable.
///
/// Tools, such as the analyzer, can provide feedback if
///
/// * the annotation is associated with a declaration not in the `lib` folder
///   of a package, or
/// * the declaration is referenced outside of its the defining library or a
///   library which is in the `test` folder of the defining package.

Solution 2:[2]

I solved this problem right now by making another public 'stub' method with the same parameters that just calls the private one and marked it with @visibleForTesting. This

@visibleForTesting
  Future<void> removeAppointments(List<DocumentSnapshot> documents, [FirebaseFirestore instance]) => _removeAppointments(documents, instance);

Solution 3:[3]

In some cases, another possibility is to separate your class into a public interface and a private implementation. For example, instead of:

class MyClass {
  final String name;

  MyClass(this.name);

  void publicMethod() {
    // ...
  }

  void _privateMethod() {
    // ...
  }
}

split it into:

my_class.dart:

import 'src/my_class_impl.dart';

abstract class MyClass {
  factory MyClass(String name) => MyClassImpl(name);

  void publicMethod();
}

src/my_class_impl.dart:

import 'package:my_package/my_class.dart';

class MyClassImpl implements MyClass {
  final String name;

  MyClassImpl(this.name);

  @override
  void publicMethod() {
    // ...
  }

  void privateMethod() {
    // ...
  }
}

Now your tests can do import 'package:my_package/src/my_class_impl.dart'; and directly access its private methods. Of course, there's nothing stopping someone else from directly importing the implementation library too, but importing files from another package's src/ directory is discouraged. Anyone doing that would be explicitly choosing to undermine encapsulation (as opposed to depending on @visibleForTesting, where breaking encapsulation could more easily be done by accident).

This approach would not be appropriate for all situations. For example, this approach also effectively makes it impossible for others to extend MyClass.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Günter Zöchbauer
Solution 2 Arghya Sadhu
Solution 3