
An automated, developer-first Visual Studio Code extension that generates 100% SonarQube compliant Unit Tests and Widget Tests for Flutter & Dart applications using BLoC state management (flutter_bloc, bloc), Cubit, Repositories, Services, and UI Widgets.
✨ Features at a Glance
- 🎯 Unit Testing & Widget Testing Support: Automatically generates unit tests for logic (
Bloc, Cubit, Repository, Service) and widget tests (StatelessWidget, StatefulWidget) with UI rendering & interaction checks.
- 🌐 Universal Networking Engine (
package:http & package:dio): Intercepts and mocks network requests at the Dart VM process level via UniversalHttpOverrides. Fully supports package:http, package:dio, dart:io HttpClient, and custom API clients.
- 🛡️ SonarQube Quality Gate Compliance: Covers initial states, happy path state transitions, and exception/error paths with mock stubbing (
when(...).thenThrow(...)) to achieve 100% line & branch coverage.
- 📦 Automatic
pubspec.yaml Inspector: Scans your project for required testing packages (flutter_test, bloc_test, mocktail) and provides a 1-Click Quick Fix to update dependencies automatically.
- 📁 Flutter Mirror Directory Mapping: Maps any source file in
lib/ (e.g., lib/features/auth/presentation/pages/login_page.dart) directly to its mirror test location (test/features/auth/presentation/pages/login_page_test.dart).
- 📊 Automated Test Execution & SonarQube Coverage Dashboard: Runs
flutter test --coverage automatically, parses coverage/lcov.info, updates status bar metrics, and presents an interactive webview dashboard.
📋 Required Developer Dependencies (pubspec.yaml)
Every developer using this extension should ensure their Flutter project's pubspec.yaml includes the following packages under dev_dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
bloc_test: ^9.1.5
mocktail: ^1.0.3
💡 Automatic Setup: If any of these packages are missing, the extension will prompt you with an interactive notification and automatically add them to your pubspec.yaml with a single click!
🚀 Quick Start Guide for Developers
Step 1: Select Your Source File
In Visual Studio Code, open any Dart file under your project's lib/ directory:
- BLoC / Cubit File: e.g.
lib/features/auth/presentation/bloc/auth_bloc.dart
- Repository / Service File: e.g.
lib/features/auth/data/auth_repository.dart
- Widget / Page File: e.g.
lib/features/auth/presentation/pages/login_page.dart
Step 2: Trigger Test Generation
Choose your preferred method:
- Explorer Context Menu: Right-click the
.dart file in VS Code Explorer -> select "Flutter BLoC: Generate Unit Tests for Selected File".
- Editor Context Menu: Right-click anywhere inside the open Dart editor.
- Command Palette: Press
Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux) -> type Flutter BLoC: Generate Unit Tests for Selected File.
Step 3: Review & Execute
- The extension creates the test file in your
test/ directory matching your exact folder structure.
- The generated test file opens side-by-side with your source code.
- The test runner automatically runs
flutter test --coverage and launches the SonarQube Coverage Dashboard showing test pass rate and line-by-line coverage score.
🧪 How Generated Tests Work
1. Generated BLoC Unit Test Example
import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:my_app/features/auth/presentation/bloc/auth_bloc.dart';
class MockAuthRepository extends Mock implements AuthRepository {}
void main() {
group('AuthBloc Tests', () {
late AuthBloc authBloc;
late MockAuthRepository mockAuthRepository;
setUp(() {
mockAuthRepository = MockAuthRepository();
authBloc = AuthBloc(authRepository: mockAuthRepository);
});
tearDown(() => authBloc.close());
test('initial state is correct', () {
expect(authBloc.state, isA<AuthInitial>());
});
group('AuthLoginRequested Event Path Coverage', () {
blocTest<AuthBloc, dynamic>(
'emits successful states when AuthLoginRequested is added',
build: () => authBloc,
setUp: () {
when(() => mockAuthRepository.login(username: any(), password: any()))
.thenAnswer((_) async => 'token_123');
},
act: (bloc) => bloc.add(AuthLoginRequested(username: 'admin', password: 'secret')),
expect: () => [isA<AuthLoading>(), isA<AuthSuccess>()],
);
blocTest<AuthBloc, dynamic>(
'emits error state when AuthLoginRequested throws Exception',
build: () => authBloc,
setUp: () {
when(() => mockAuthRepository.login(username: any(), password: any()))
.thenThrow(Exception('Server unreachable'));
},
act: (bloc) => bloc.add(AuthLoginRequested(username: 'admin', password: 'secret')),
expect: () => [isA<AuthLoading>(), isA<AuthFailure>()],
);
});
});
}
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:my_app/features/auth/presentation/pages/login_page.dart';
void main() {
group('LoginPage Widget Tests', () {
testWidgets('renders LoginPage component cleanly', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: LoginPage())),
);
await tester.pump();
expect(find.byType(LoginPage), findsOneWidget);
});
testWidgets('triggers user action on interaction', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: LoginPage())),
);
await tester.pumpAndSettle();
final buttonFinder = find.byType(ElevatedButton);
if (buttonFinder.evaluate().isNotEmpty) {
await tester.tap(buttonFinder.first);
await tester.pump();
}
});
});
}
📊 SonarQube Coverage Integration
The generated tests produce standard LCOV coverage files at coverage/lcov.info.
To include coverage in your SonarQube pipeline, add the following to your sonar-project.properties:
sonar.projectKey=my_flutter_app
sonar.sources=lib
sonar.tests=test
sonar.dart.coverage.reportPath=coverage/lcov.info
⚙️ Extension Settings
Customize extension preferences in Settings (Cmd+, / Ctrl+,) under Flutter BLoC Unit Test Generator:
| Setting |
Type |
Default |
Description |
flutterBlocTestGen.mockingFramework |
string |
mocktail |
Choose mocking framework (mocktail or mockito). |
flutterBlocTestGen.autoRunTestsAfterGeneration |
boolean |
true |
Automatically run flutter test --coverage after generation. |
flutterBlocTestGen.coverageThreshold |
number |
100 |
Target percentage threshold for SonarQube quality gate. |
🛠️ Marketplace Publishing & Packaging
To package or publish this extension:
# Package extension into .vsix file
npx @vscode/vsce package
# Publish directly to VS Code Marketplace
npx @vscode/vsce publish
📄 License
This extension is licensed under the MIT License.