Token导航 LogoToken导航

Agent Skill · 研究检索

flutter-testing(Flutter 测试)

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

flutter-testing 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 64,068。

总安装 306周安装 13GitHub Stars 6下载 107核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:flutter-testing(Flutter 测试)
来源仓库:https://github.com/vp-k/flutter-craft
仓库路径:skills/flutter-testing
安装命令:
npx skills add https://github.com/vp-k/flutter-craft --skill flutter-testing
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/vp-k/flutter-craft --skill flutter-testing

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

flutter-testing 用于辅助测试设计、自动化测试、用例整理和回归验证。适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑。涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/flutter-testing

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/vp-k/flutter-craft --skill flutter-testing;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

flutter-testing 怎么安装?

在终端执行 npx skills add https://github.com/vp-k/flutter-craft --skill flutter-testing。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

flutter-testing 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

flutter-testing 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

flutter-testing 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

flutter-testing 用的人多吗?

公开数据是累计安装 306 次,近一周安装 13 次,下载 107 次,GitHub 6 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

flutter-testing 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
flutter-testing

Flutter 测试 · 本页

3066需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Flutter Testing Guide

Overview

Write tests following priority order after implementation. Focus on business logic first, UI last.

Announce at start: "I'm using the flutter-testing skill to write tests."

Test Priority Order

Priority 1: Repository & DataSource Unit Tests
  ├── Business logic correctness
  ├── API integration
  └── Data transformation

Priority 2: State Management Unit Tests
  ├── BLoC/Cubit event handling
  ├── Provider state transitions
  └── Error state handling

Priority 3: Widget Tests (Optional)
  ├── User interactions
  ├── Widget rendering
  └── Navigation

Priority 4: Golden Tests (Visual Regression)
  ├── Design system component snapshots
  ├── Pixel-perfect comparison
  └── CI integration

Optional: Integration Tests
  └── Full app flow testing

Priority 1: Repository & DataSource Tests

Repository Test Template

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';

@GenerateMocks([UserRemoteDataSource, UserLocalDataSource])
import 'user_repository_impl_test.mocks.dart';

void main() {
  late UserRepositoryImpl repository;
  late MockUserRemoteDataSource mockRemoteDataSource;
  late MockUserLocalDataSource mockLocalDataSource;

  setUp(() {
    mockRemoteDataSource = MockUserRemoteDataSource();
    mockLocalDataSource = MockUserLocalDataSource();
    repository = UserRepositoryImpl(
      remoteDataSource: mockRemoteDataSource,
      localDataSource: mockLocalDataSource,
    );
  });

  group('getUser', () {
    const tUserId = '123';
    final tUserModel = UserModel(id: '123', name: 'Test', email: 'test@test.com');
    final tUserEntity = User(id: '123', name: 'Test', email: 'test@test.com');

    test('should return User when remote data source succeeds', () async {
      // Arrange
      when(mockRemoteDataSource.getUser(any))
          .thenAnswer((_) async => tUserModel);

      // Act
      final result = await repository.getUser(tUserId);

      // Assert
      expect(result, equals(tUserEntity));
      verify(mockRemoteDataSource.getUser(tUserId));
    });

    test('should throw Exception when remote data source fails', () async {
      // Arrange
      when(mockRemoteDataSource.getUser(any))
          .thenThrow(Exception('Server error'));

      // Act & Assert
      expect(
        () => repository.getUser(tUserId),
        throwsException,
      );
    });
  });
}

DataSource Test Template

import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/mockito.dart';

@GenerateMocks([http.Client])
import 'user_remote_datasource_test.mocks.dart';

void main() {
  late UserRemoteDataSourceImpl dataSource;
  late MockClient mockHttpClient;

  setUp(() {
    mockHttpClient = MockClient();
    dataSource = UserRemoteDataSourceImpl(client: mockHttpClient);
  });

  group('getUser', () {
    const tUserId = '123';
    final tUserJson = '{"id": "123", "name": "Test", "email": "test@test.com"}';

    test('should return UserModel when response is 200', () async {
      // Arrange
      when(mockHttpClient.get(any, headers: anyNamed('headers')))
          .thenAnswer((_) async => http.Response(tUserJson, 200));

      // Act
      final result = await dataSource.getUser(tUserId);

      // Assert
      expect(result, isA<UserModel>());
      expect(result.id, equals('123'));
    });

    test('should throw ServerException when response is not 200', () async {
      // Arrange
      when(mockHttpClient.get(any, headers: anyNamed('headers')))
          .thenAnswer((_) async => http.Response('Error', 500));

      // Act & Assert
      expect(
        () => dataSource.getUser(tUserId),
        throwsA(isA<ServerException>()),
      );
    });
  });
}

Priority 2: State Management Tests

BLoC Test Template

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

@GenerateMocks([GetUserUseCase])
import 'user_bloc_test.mocks.dart';

void main() {
  late UserBloc bloc;
  late MockGetUserUseCase mockGetUser;

  setUp(() {
    mockGetUser = MockGetUserUseCase();
    bloc = UserBloc(getUser: mockGetUser);
  });

  tearDown(() {
    bloc.close();
  });

  test('initial state should be UserInitial', () {
    expect(bloc.state, equals(UserInitial()));
  });

  blocTest<UserBloc, UserState>(
    'should emit [Loading, Loaded] when GetUser succeeds',
    build: () {
      when(mockGetUser(any))
          .thenAnswer((_) async => const User(id: '1', name: 'Test'));
      return bloc;
    },
    act: (bloc) => bloc.add(const GetUserEvent('1')),
    expect: () => [
      UserLoading(),
      const UserLoaded(User(id: '1', name: 'Test')),
    ],
  );

  blocTest<UserBloc, UserState>(
    'should emit [Loading, Error] when GetUser fails',
    build: () {
      when(mockGetUser(any)).thenThrow(Exception('error'));
      return bloc;
    },
    act: (bloc) => bloc.add(const GetUserEvent('1')),
    expect: () => [
      UserLoading(),
      const UserError('error'),
    ],
  );
}

Provider/Riverpod Test Template

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mockito/mockito.dart';

void main() {
  late ProviderContainer container;
  late MockUserRepository mockRepository;

  setUp(() {
    mockRepository = MockUserRepository();
    container = ProviderContainer(
      overrides: [
        userRepositoryProvider.overrideWithValue(mockRepository),
      ],
    );
  });

  tearDown(() {
    container.dispose();
  });

  test('should return user when fetchUser succeeds', () async {
    // Arrange
    when(mockRepository.getUser(any))
        .thenAnswer((_) async => const User(id: '1', name: 'Test'));

    // Act
    final result = await container.read(userProvider('1').future);

    // Assert
    expect(result.name, equals('Test'));
  });
}

Priority 3: Widget Tests (Optional)

Widget Test Template

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mockito/mockito.dart';

void main() {
  late MockUserBloc mockBloc;

  setUp(() {
    mockBloc = MockUserBloc();
  });

  Widget createWidgetUnderTest() {
    return MaterialApp(
      home: BlocProvider<UserBloc>.value(
        value: mockBloc,
        child: const UserScreen(),
      ),
    );
  }

  testWidgets('should display loading indicator when state is Loading',
      (tester) async {
    // Arrange
    when(mockBloc.state).thenReturn(UserLoading());

    // Act
    await tester.pumpWidget(createWidgetUnderTest());

    // Assert
    expect(find.byType(CircularProgressIndicator), findsOneWidget);
  });

  testWidgets('should display user name when state is Loaded',
      (tester) async {
    // Arrange
    when(mockBloc.state).thenReturn(
      const UserLoaded(User(id: '1', name: 'John Doe')),
    );

    // Act
    await tester.pumpWidget(createWidgetUnderTest());

    // Assert
    expect(find.text('John Doe'), findsOneWidget);
  });

  testWidgets('should call GetUserEvent when button is tapped',
      (tester) async {
    // Arrange
    when(mockBloc.state).thenReturn(UserInitial());

    // Act
    await tester.pumpWidget(createWidgetUnderTest());
    await tester.tap(find.byType(ElevatedButton));

    // Assert
    verify(mockBloc.add(any)).called(1);
  });
}

Priority 4: Golden Tests (Visual Regression)

디자인 시스템 컴포넌트의 시각적 일관성을 검증합니다. UI가 의도치 않게 변경되는 것을 방지합니다.

Golden Test Template

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('LoginButton matches golden', (tester) async {
    // 고정 크기로 일관된 스크린샷 보장
    await tester.binding.setSurfaceSize(const Size(400, 200));

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: LoginButton(onPressed: () {}),
          ),
        ),
      ),
    );

    await expectLater(
      find.byType(LoginButton),
      matchesGoldenFile('goldens/login_button.png'),
    );
  });

  testWidgets('UserCard dark mode matches golden', (tester) async {
    await tester.binding.setSurfaceSize(const Size(400, 300));

    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.dark(),
        home: Scaffold(
          body: UserCard(
            user: User(id: '1', name: 'Test User', avatar: 'assets/test/avatar.png'),
          ),
        ),
      ),
    );

    await expectLater(
      find.byType(UserCard),
      matchesGoldenFile('goldens/user_card_dark.png'),
    );
  });
}

Golden File Management

# 최초 생성 / 기준선 업데이트 (의도적 변경 후)
flutter test --update-goldens

# 비교 실행 (CI/CD에서 사용)
flutter test --tags golden

# 특정 파일만
flutter test test/features/auth/presentation/widgets/login_button_golden_test.dart --update-goldens

테스트 파일 명명 규칙

골든 테스트 파일은 _golden_test.dart 접미사로 구분:

test/features/auth/presentation/widgets/
├── login_button_test.dart         # 기능 테스트 (Priority 3)
└── login_button_golden_test.dart  # 골든 테스트 (Priority 4)

태그 기반 실행

dart_test.yaml에서 골든 테스트를 태그로 분리:

tags:
  golden:
    # CI에서 별도 실행 가능

테스트 파일에 태그 추가:

@Tags(['golden'])
library;

import 'package:flutter_test/flutter_test.dart';
// ...

Golden Test 작성 시 주의사항

  1. 고정 크기: setSurfaceSize()로 일관된 캔버스 크기 설정
  2. 결정적 데이터: 랜덤/시간 의존 데이터 사용 금지
  3. 폰트 로딩: 커스텀 폰트는 FontLoader로 미리 로드
  4. 네트워크 이미지: Mock으로 대체 (네트워크 의존성 제거)
  5. 플랫폼 차이: macOS/Linux/Windows에서 렌더링이 다를 수 있음 → CI 환경 고정

언제 Golden Test를 작성하는가

  • 디자인 시스템 컴포넌트 (Button, Card, Input, Badge 등)
  • 테마 변경 후 시각적 영향 검증
  • 다크 모드 / 라이트 모드 전환 검증
  • 반응형 레이아웃의 브레이크포인트별 검증

언제 Golden Test를 건너뛰는가

  • 비즈니스 로직 위주 화면 (데이터 표시만 다름)
  • 자주 변경되는 화면 (골든 파일 업데이트 비용 > 이득)
  • 외부 데이터에 크게 의존하는 화면

Test File Structure

test/
├── features/
│   └── auth/
│       ├── data/
│       │   ├── datasources/
│       │   │   └── auth_remote_datasource_test.dart
│       │   └── repositories/
│       │       └── auth_repository_impl_test.dart
│       └── presentation/
│           ├── bloc/
│           │   └── auth_bloc_test.dart
│           └── widgets/
│               └── login_button_test.dart
└── helpers/
    ├── test_helpers.dart
    └── pump_app.dart

Running Tests

# All tests
flutter test

# Specific feature
flutter test test/features/auth/

# Specific file
flutter test test/features/auth/data/repositories/auth_repository_impl_test.dart

# With coverage
flutter test --coverage

# Generate coverage report
genhtml coverage/lcov.info -o coverage/html

Test Dependencies

# Mocking (choose one)
flutter pub add dev:mockito        # Requires codegen
flutter pub add dev:mocktail       # No codegen required (recommended)

# Code generation
flutter pub add dev:build_runner

# State management testing
flutter pub add dev:bloc_test      # If using BLoC
flutter pub add dev:riverpod_test  # If using Riverpod (optional)

# Freezed (if using immutable states)
flutter pub add freezed_annotation
flutter pub add dev:freezed

Generate Mocks

# Generate mock files
flutter pub run build_runner build --delete-conflicting-outputs

Key Principles

  1. Priority Order: Repository → State → Widget
  2. Mock Dependencies: Don't test real APIs or databases
  3. Arrange-Act-Assert: Clear test structure
  4. One Assertion Focus: Each test tests one thing
  5. Descriptive Names: Test names describe behavior

When to Skip Tests

  • Skip Widget Tests when:

- Simple stateless widgets - No user interaction logic - Pure presentation (no business logic)

  • Never Skip:

- Repository tests (business logic) - State management tests (state transitions) - Error handling tests

REQUIRED SUB-SKILL

After writing tests, you MUST invoke: → flutter-craft:flutter-verification

Run flutter test and verify all tests pass.

平台分布

Codex

36.91%
按下载量换算39

Claude

28.7%
按下载量换算31

Cursor

19.25%
按下载量换算21

Gemini CLI

8.4%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills