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
.