Skip to content

Instantly share code, notes, and snippets.

@tomaszpolanski
Created March 25, 2025 12:39
Show Gist options
  • Save tomaszpolanski/abd4725d4e07ba637d5540b5fe10aa1e to your computer and use it in GitHub Desktop.
Save tomaszpolanski/abd4725d4e07ba637d5540b5fe10aa1e to your computer and use it in GitHub Desktop.
ArrangeBuilder and test mixins for Dart
import 'example.dart';
import 'package:mocktail/mocktail.dart';
/// An example of a mixing that needs to be implemented once and can be used
/// in multiple tests without doing `when(() => ...)` in every test file
mixin ApiMixin {
Api api = _MockApi();
/// Function that prepares the mixin to be in a usable state.
/// This would be mixin's constructor if mixins would support constructor
void arrangeApi() {
withApiSuccess();
}
void withApiSuccess([String? message]) {
when(() => api.fetch(any())).thenAnswer((_) async => message ?? 'empty');
}
void withApiError() {
when(() => api.fetch(any())).thenThrow(Exception('some exception'));
}
}
class _MockApi extends Mock implements Api {}
class ExampleUsecase {
ExampleUsecase({required Api api}) : _api = api;
final Api _api;
Future<String> fetchMessage() async {
try {
final message = await _api.fetch(42);
return message;
} catch (_) {
return 'Something went wrong';
}
}
}
/// Api that fetches some data. For testing [ExampleUsecase] we do not need
/// to have a concrete implementation as we are testing behavior of [ExampleUsecase]
abstract class Api {
Future<String> fetch(int id);
}
import 'api_mixin.dart';
import 'example.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
late _ArrangeBuilder _builder;
void main() {
setUp(() {
_builder = _ArrangeBuilder();
});
test('returns error message when failed', () async {
_builder.withApiError();
final tested = _builder.build();
final result = await tested.fetchMessage();
expect(result, 'Something went wrong');
});
test('returns message message when successful', () async {
const expected = 'some message';
_builder.withApiSuccess(expected);
final tested = _builder.build();
final result = await tested.fetchMessage();
expect(result, expected);
});
test('calls api with a specific id', () async {
const expected = 42;
final tested = _builder.build();
await tested.fetchMessage();
verify(() => _builder.api.fetch(expected)).called(1);
});
}
/// ArrangeBuilder can use multiple mixins
class _ArrangeBuilder with ApiMixin {
_ArrangeBuilder() {
arrangeApi();
}
ExampleUsecase build() => ExampleUsecase(api: api);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment