I am trying to write a mockable client for our backend system.
Our setup looks like this:
MyBackend
┣ Base
┃ ┣ my_client.dart
┃ ┣ int_repo.dart
┃ ┗ string_repo.dart
┣ Default Impl
┃ ┣ my_client_impl.dart
┃ ┣ int_repo_impl.dart
┃ ┗ string_repo_impl.dart
┣ Mock Impl
┃ ┣ etc...
What I am trying to accomplish looks something like this:
abstract class MyClient with IntRepo, StringRepo {
MyClientConfig get config;
}
class MyClientImpl with IntRepoImpl, StringRepoImpl {
@override
final MyClientConfig config = ...;
// No logic needed to support int or string repos
}
class MyClientMock with IntRepoMock, StringRepoMock {
@override
final MyClientConfig config = ...;
// No logic needed to support int or string repos
}
Which would have end points defined as so:
abstract mixin StringRepo {
Future<String> getString();
@override
Future<String> getString() => ...;
}
mixin StringRepoMock extends StringRepo {
/// Some end point that depends on `MyClientMock.config`
@override
Future<String> getString() => ...;
}
Which would be called like this:
void main() async {
final client = MyClientImpl(...);
// final client = MyClientMock(...);
final value = await client.getString();
}
However, this pseudo-code depends on being able to define the abstract mixins IntRepo and StringRepo.
I got close to the behavior I'm looking for by doing the following:
abstract interface class StringRepo {
Future<String> getString();
}
mixin StringRepoImpl on MyClient implements StringRepo {
/// Some end point that depends on `MyClientImpl.config`
@override
Future<String> getString() => ...;
}
Unfortunately, this doesn't seem to be possible, since mixins can't be abstract, nor can they extend each other.
The issue I have with this work-around is that the burden of the interface is borne on MyClient, meaning that if I add getAnotherString(), the error is thrown in my_client_impl.dart, rather than in string_repo_impl.dart.
In other words, MyClientImpl is required to implement getAnotherString(), but I want to force StringRepoImpl to implement it instead.
Am I trying to reinvent the wheel here or is there some language feature that dart is missing that can do this?