plugins/test-generator/skills/assertion-helper/SKILL.md
分析函数逻辑并生成合适的断言。 当用户说"生成断言"、"写断言"、"expect语句"、"assert断言"、"验证返回值"、"测试验证"、"断言代码"时使用此技能。 支持多框架断言生成:Jest (expect)、Vitest (expect)、Python (assert/pytest)。 生成类型断言、边界值验证、深度对象比较、Mock调用验证、副作用验证、异步断言。输出精确、可维护的断言代码。
npx skillsauth add protagonistss/ithinku-plugins assertion-helperInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
这个技能负责分析函数逻辑并生成合适的断言,确保测试能够准确验证代码的行为。
// 为函数生成断言
await generateAssertions('calculateTotal', {
inputs: [{ items: [{ price: 10, qty: 2 }] }],
framework: 'vitest'
});
// 生成类方法的断言
await generateAssertions('UserService.createUser', {
context: 'class',
framework: 'vitest'
});
await generateAssertions(target, {
framework: 'pytest',
assertionStyle: 'strict', // 'strict' | 'loose' | 'custom'
includeSideEffects: true,
verifyMocks: true,
customAssertions: {
'shouldBeValidUser': 'expect(user).toMatchObject({ id: expect.any(Number), name: expect.any(String) })'
}
});
// 基础类型断言
{{#if returns}}
{{#eq returns.type 'string'}}
expect(result).toBeString();
expect(result).toHaveLength({{#if returns.length}}{{returns.length}}{{else}}greaterThan(0){{/if}});
{{/eq}}
{{#eq returns.type 'number'}}
expect(result).toBeNumber();
expect(result).{{#if returns.range}}toBeGreaterThanOrEqual({{returns.range.min}});
expect(result).toBeLessThanOrEqual({{returns.range.max}});{{/if}}
{{/eq}}
{{#eq returns.type 'boolean'}}
expect(result).toBeBoolean();
{{/eq}}
{{#eq returns.type 'array'}}
expect(result).toBeArray();
expect(result).toHaveLength({{#if returns.length}}{{returns.length}}{{else}}greaterThan(0){{/if}});
{{#if returns.items}}
expect(result[0]).toMatchObject({{{json returns.items}}});
{{/if}}
{{/eq}}
{{#eq returns.type 'object'}}
expect(result).toBeObject();
{{#if returns.properties}}
expect(result).toMatchObject({
{{#each returns.properties}}
{{@key}}: {{#if this.type}}expect.any({{camelCase this.type}}){{else}}{{{json this}}}{{/if}},
{{/each}}
});
{{/if}}
{{/eq}}
{{/if}}
// 异步断言
{{#if isAsync}}
await expect(asyncFunction()).resolves.toBe(expectedValue);
await expect(asyncFunction()).resolves.not.toThrow();
// 错误断言
await expect(asyncFunction()).rejects.toThrow('Expected error message');
{{/if}}
// Mock验证断言
{{#each mocks}}
{{#if this.shouldBeCalled}}
expect({{name}}).toHaveBeenCalled();
expect({{name}}).toHaveBeenCalledTimes({{this.callCount}});
{{#if this.withParams}}
expect({{name}}).toHaveBeenCalledWith({{#each this.withParams}}{{this}}{{#unless @last}}, {{/unless}}{{/each}});
{{/if}}
{{else}}
expect({{name}}).not.toHaveBeenCalled();
{{/if}}
{{/each}}
// 副作用验证
{{#if sideEffects}}
{{#each sideEffects}}
{{#eq this.type 'console'}}
// Console输出验证
expect(console.{{this.method}}).toHaveBeenCalledWith({{{json this.args}}});
{{/eq}}
{{#eq this.type 'event'}}
// 事件触发验证
expect(eventEmitter.emit).toHaveBeenCalledWith('{{this.eventName}}', {{#if this.payload}}{{{json this.payload}}}{{else}}expect.any(Object){{/if}});
{{/eq}}
{{#eq this.type 'state'}}
// 状态变化验证
expect(component.state.{{this.property}}).toBe({{{json this.value}}});
{{/eq}}
{{/each}}
{{/if}}
import { expect } from 'vitest';
// 类型断言
{{#if returns}}
expect(result).toBeDefined();
{{#eq returns.type 'string'}}
expect(typeof result).toBe('string');
{{/eq}}
{{#eq returns.type 'number'}}
expect(typeof result).toBe('number');
{{/eq}}
{{#eq returns.type 'object'}}
expect(typeof result).toBe('object');
expect(result).not.toBeNull();
{{/eq}}
{{/if}}
// 精确匹配
expect(result).toEqual(expectedValue);
// 部分匹配
expect(result).toMatchObject(partialExpected);
// 数组断言
expect(result).toContain(expectedItem);
expect(result).toHaveLength(expectedLength);
// 异常断言
expect(() => functionThatThrows()).toThrow();
expect(() => functionThatThrows()).toThrowError('Error message');
# 基础断言
assert result is not None
{{#eq returns.type 'string'}}
assert isinstance(result, str)
assert len(result) > 0
{{/eq}}
{{#eq returns.type 'number'}}
assert isinstance(result, (int, float))
assert result >= {{#if returns.min}}{{returns.min}}{{else}}0{{/if}}
{{/eq}}
{{#eq returns.type 'list'}}
assert isinstance(result, list)
assert len(result) > 0
{{#if returns.items}}
assert result[0] == {{returns.items}}
{{/if}}
{{/eq}}
{{#eq returns.type 'dict'}}
assert isinstance(result, dict)
{{#if returns.keys}}
for key in {{json returns.keys}}:
assert key in result
{{/if}}
{{/eq}}
# 异常断言
with pytest.raises({{errorType}}, match="{{errorMessage}}"):
function_that_raises()
# Mock验证
{{#each mocks}}
{{#if this.shouldBeCalled}}
assert {{name}}.called
assert {{name}}.call_count == {{this.callCount}}
{{#if this.withArgs}}
{{name}}.assert_called_with({{#each this.withArgs}}{{this}}{{#unless @last}}, {{/unless}}{{/each}})
{{/if}}
{{else}}
assert not {{name}}.called
{{/if}}
{{/each}}
class AssertionGenerator {
static generateForReturn(returnType: TypeInfo, value?: any): string[] {
const assertions: string[] = [];
// 类型断言
switch (returnType.type) {
case 'string':
assertions.push('expect(typeof result).toBe("string")');
if (value) {
assertions.push(`expect(result).toBe("${value}")`);
}
break;
case 'number':
assertions.push('expect(typeof result).toBe("number")');
if (returnType.constraints?.min !== undefined) {
assertions.push(`expect(result).toBeGreaterThanOrEqual(${returnType.constraints.min})`);
}
if (returnType.constraints?.max !== undefined) {
assertions.push(`expect(result).toBeLessThanOrEqual(${returnType.constraints.max})`);
}
break;
case 'boolean':
assertions.push('expect(typeof result).toBe("boolean")');
break;
case 'array':
assertions.push('expect(Array.isArray(result)).toBe(true)');
if (value?.length) {
assertions.push(`expect(result).toHaveLength(${value.length})`);
}
break;
case 'object':
assertions.push('expect(typeof result).toBe("object")');
assertions.push('expect(result).not.toBeNull()');
if (value) {
assertions.push(`expect(result).toMatchObject(${JSON.stringify(value)})`);
}
break;
}
return assertions;
}
static generateBoundaryTests(
functionName: string,
params: ParameterInfo[]
): string[] {
const tests: string[] = [];
params.forEach(param => {
if (param.constraints?.min !== undefined) {
tests.push(`
it('should handle minimum value for ${param.name}', () => {
const result = ${functionName}(${param.constraints.min});
expect(result).toBeDefined();
});
`);
}
if (param.constraints?.max !== undefined) {
tests.push(`
it('should handle maximum value for ${param.name}', () => {
const result = ${functionName}(${param.constraints.max});
expect(result).toBeDefined();
});
`);
}
});
return tests;
}
}
// 错误场景断言生成
function generateErrorAssertions(functionInfo) {
const assertions = [];
// 可能的错误条件
if (functionInfo.params.some(p => p.required)) {
assertions.push(`
it('should throw error when required parameter is missing', () => {
expect(() => ${functionInfo.name}()).toThrow();
expect(() => ${functionInfo.name}(null)).toThrow();
});
`);
}
if (functionInfo.params.some(p => p.type === 'number')) {
assertions.push(`
it('should handle invalid number inputs', () => {
expect(() => ${functionInfo.name}(NaN)).toThrow();
expect(() => ${functionInfo.name}(Infinity)).toThrow();
});
`);
}
return assertions;
}
// 异步函数断言生成
function generateAsyncAssertions(functionInfo) {
return `
describe('async behavior', () => {
it('should resolve with expected value', async () => {
const result = await ${functionInfo.name}(${generateValidParams(functionInfo)});
expect(result).toBeDefined();
${generateReturnAssertions(functionInfo.returnType)}
});
it('should handle rejection', async () => {
// 测试拒绝情况
await expect(${functionInfo.name}(${generateErrorParams()}))
.rejects.toThrow();
});
it('should handle timeout', async () => {
// 如果有超时配置
await expect(${functionInfo.name}(${generateSlowParams()}))
.rejects.toThrow('timeout');
});
});
`;
}
// 深度匹配断言
function generateDeepObjectAssertions(obj: any, path: string = ''): string[] {
const assertions: string[] = [];
for (const [key, value] of Object.entries(obj)) {
const currentPath = path ? `${path}.${key}` : key;
if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
assertions.push(`
expect(result.${currentPath}).toBeArray();
expect(result.${currentPath}).toHaveLength(${value.length});
`);
} else {
assertions.push(`
expect(result.${currentPath}).toBeObject();
`);
// 递归处理嵌套对象
assertions.push(...generateDeepObjectAssertions(value, currentPath));
}
} else {
assertions.push(`
expect(result.${currentPath}).toBe(${JSON.stringify(value)});
`);
}
}
return assertions;
}
// Mock函数调用断言
function generateMockAssertions(mocks: MockInfo[]): string[] {
return mocks.map(mock => {
const assertions: string[] = [];
if (mock.expectCall) {
assertions.push(`expect(${mock.name}).toHaveBeenCalled();`);
if (mock.expectedCallCount) {
assertions.push(
`expect(${mock.name}).toHaveBeenCalledTimes(${mock.expectedCallCount});`
);
}
if (mock.expectedArgs) {
assertions.push(
`expect(${mock.name}).toHaveBeenCalledWith(${mock.expectedArgs.join(', ')});`
);
}
if (mock.expectedCalls) {
mock.expectedCalls.forEach((call, index) => {
assertions.push(
`expect(${mock.name}).toHaveBeenNthCalledWith(${index + 1}, ${call.join(', ')});`
);
});
}
} else {
assertions.push(`expect(${mock.name}).not.toHaveBeenCalled();`);
}
return assertions.join('\n');
});
}
// 副作用断言生成
function generateSideEffectAssertions(sideEffects: SideEffect[]): string[] {
return sideEffects.map(effect => {
switch (effect.type) {
case 'console':
return `expect(console.${effect.method}).toHaveBeenCalledWith(${JSON.stringify(effect.args)});`;
case 'state':
return `expect(${effect.target}).toBe(${JSON.stringify(effect.value)});`;
case 'event':
return `expect(${effect.emitter}.emit).toHaveBeenCalledWith('${effect.event}', ${JSON.stringify(effect.payload)});`;
case 'storage':
return `expect(localStorage.getItem('${effect.key}')).toBe('${effect.value}');`;
default:
return '';
}
}).filter(Boolean);
}
development
Vue 3 开发最佳实践指南 - Composition API、Script Setup、Pinia、TypeScript 集成及性能优化。 当用户说"Vue 3组件"、"Composition API"、"script setup"、"Pinia"、"Vue 3项目"、"ref reactive"、"defineProps defineEmits"、"Composable"、"Vue 3优化"时使用此技能。 涵盖:Script Setup 与 Composition API、响应式数据选择(ref vs reactive)、组件通信(Props/Emits/v-model/Slots)、Composables 设计模式、Pinia Setup Store、性能优化(v-memo、shallowRef、KeepAlive)。 提供 TypeScript 代码示例、反模式对照表、迁移指南和示例文件引用。
development
Vue 2 维护与开发最佳实践指南 - Options API、Vuex 及向 Vue 3 迁移准备。 当用户说"Vue 2组件"、"Options API"、"Vuex"、"Vue 2项目"、"Vue 2迁移"、"Vue mixin"、"Vue 2最佳实践"时使用此技能。 涵盖:Options API 规范(选项顺序、props 验证)、Vuex 模块化(namespaced modules)、逻辑复用(避免 mixin,使用工具函数)、迁移准备(停止使用 Filters、引入 Composition API 插件)。 提供 Vue 2 代码示例、反模式警告和迁移建议。
development
核心设计能力 - 提供配色、布局、组件样式生成及反模式检查。 当用户说"设计UI"、"生成样式"、"页面布局"、"CSS样式"、"组件设计"、"配色方案"、"设计系统"、"前端样式"、"响应式设计"、"动画效果"时使用此技能。 支持多种设计风格:Neo-Brutalism、Glassmorphism、Editorial、Cyberpunk。 提供配色方案、布局生成、组件样式、微交互动效、响应式网格。拒绝"AI廉价感",追求大胆、独特、细节丰富的设计。 重要特性:提供反模式检查,避免泛滥的渐变、无聊的阴影、默认圆角等平庸设计。
content-media
无障碍设计审查与修复能力。 当用户说"无障碍"、"a11y"、"WCAG"、"键盘导航"、"屏幕阅读器"、"颜色对比度"、"ARIA"、"可访问性"、"辅助功能"、"盲人友好"时使用此技能。 基于 WCAG 2.1 标准,检测图片 Alt 文本缺失、表单 Label 关联、键盘可访问性、颜色对比度不足、ARIA 属性误用等问题。 提供修复代码示例:语义化标签、焦点管理、焦点陷阱、屏幕阅读器支持。输出合规性检查报告和修复建议。