Skip to main content

testing

Unit/integration testing standards for RedisInsight using Jest and Testing Library: test structure, the `renderComponent` helper, faker for test data, mocking patterns, and `waitFor` instead of fixed time waits. Use when writing or modifying any `*.spec.ts` or `*.spec.tsx` file, when adding component or slice tests, when debugging flaky tests, or when the user mentions jest, testing library, faker, or test patterns.

ソース情報

リポジトリ
redis/RedisInsight
ソースの最終更新活動
2026年6月22日 11:41
検出された SKILL.md の言語
英語
スター
8,917
フォーク
489

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
testing
description
Unit/integration testing standards for RedisInsight using Jest and Testing Library: test structure, the `renderComponent` helper, faker for test data, mocking patterns, and `waitFor` instead of fixed time waits. Use when writing or modifying any `*.spec.ts` or `*.spec.tsx` file, when adding component or slice tests, when debugging flaky tests, or when the user mentions jest, testing library, faker, or test patterns.
# Testing Standards and Practices ## Core Principles - **Write tests for all new features** - Follow **AAA pattern**: Arrange, Act, Assert - Use **descriptive test names**: "should do X when Y" - **CRITICAL**: Never use fixed time waits - tests must be deterministic - **CRITICAL**: Use faker library (@faker-js/faker) for test data ## Test Organization ```typescript describe('FeatureService', () => { describe('findById', () => { it('should return entity when found', () => {}); it('should throw NotFoundException when not found', () => {}); }); describe('create', () => { it('should create entity with valid data', () => {}); it('should throw error with invalid data', () => {}); }); }); ``` ## Frontend Testing (Jest + Testing Library) ### Running Specific Tests ```bash # Run a specific test file node 'node_modules/.bin/jest' 'redisinsight/ui/src/path/to/Component.spec.tsx' -c 'jest.config.cjs' # Run a specific test by name (use -t flag) node 'node_modules/.bin/jest' 'redisinsight/ui/src/path/to/Component.spec.tsx' -c 'jest.config.cjs' -t 'test name pattern' # Example: node 'node_modules/.bin/jest' 'redisinsight/ui/src/slices/tests/browser/keys.spec.ts' -c 'jest.config.cjs' -t 'refreshKeyInfoAction' ``` ### CRITICAL: Always Use Shared `renderComponent` Helper **Create a `renderComponent` helper for each component test file**: ```typescript import { faker } from '@faker-js/faker'; // Pull structured data from a shared factory (redisinsight/ui/src/mocks/factories/); // only build ad-hoc primitives/callbacks inline. describe('MyComponent', () => { // Default props: factory for the entity, inline only for callbacks/primitives const mockUser = UserFactory.build(); const defaultProps: MyComponentProps = { user: mockUser, onComplete: jest.fn(), }; // Shared render helper const renderComponent = (propsOverride?: Partial<MyComponentProps>) => { const props = { ...defaultProps, ...propsOverride }; return render( <Provider store={store}> <MyComponent {...props} /> </Provider> ); }; beforeEach(() => { jest.clearAllMocks(); }); it('should render component', () => { renderComponent(); expect(screen.getByText(defaultProps.name)).toBeInTheDocument(); }); it('should handle click', async () => { const mockOnComplete = jest.fn(); renderComponent({ onComplete: mockOnComplete }); fireEvent.click(screen.getByRole('button')); await waitFor(() => { expect(mockOnComplete).toHaveBeenCalledTimes(1); }); }); }); ``` **Benefits**: - Centralized setup (providers, router, theme) - Default props defined once - Easy prop overrides per test - No duplicate setup code ### Complex Component Setup For components requiring Router, ThemeProvider, etc., include them in `renderComponent`: ```typescript const renderComponent = (propsOverride?: Partial<Props>) => { const props = { ...defaultProps, ...propsOverride } return render( <Provider store={store}> <BrowserRouter> <ThemeProvider theme={theme}> <Component {...props} /> </ThemeProvider> </BrowserRouter> </Provider> ) } ``` ### Testing with Redux Create a test store with `configureStore` for Redux-connected components: ```typescript const createTestStore = (initialState = {}) => { return configureStore({ reducer: { user: userSlice.reducer }, preloadedState: initialState, }); }; const renderComponent = (propsOverride?: Partial<Props>, storeState = {}) => { const testStore = createTestStore(storeState); // render with testStore }; ``` ### Query Priorities (Testing Library) **Prefer accessible queries** (as users would interact): ```typescript // ✅ PREFERRED screen.getByRole('button', { name: /submit/i }); screen.getByLabelText('Email'); screen.getByPlaceholderText('Enter name'); // ⚠️ LAST RESORT screen.getByTestId('user-profile'); // ❌ AVOID wrapper.find('.button-class'); ``` ### Testing Async Behavior ```typescript // ✅ GOOD: waitFor with proper queries await waitFor(() => { expect(screen.getByText('Data loaded')).toBeInTheDocument(); }); // ✅ GOOD: waitForElementToBeRemoved await waitForElementToBeRemoved(() => screen.queryByText('Loading...')); // ✅ GOOD: findBy queries (built-in waiting) const element = await screen.findByText('Async content'); // ❌ BAD: Fixed timeouts (flaky tests) await new Promise((resolve) => setTimeout(resolve, 1000)); ``` ### Mocking API Calls (MSW) Use Mock Service Worker for API mocking: ```typescript import { rest } from 'msw'; import { setupServer } from 'msw/node'; const server = setupServer( rest.get('/api/users/:id', (req, res, ctx) => { return res( ctx.json({ id: req.params.id, name: faker.person.fullName(), }), ); }), ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); ``` ## Backend Testing (NestJS/Jest) ### Service Test Pattern ```typescript import { Factory } from 'fishery'; import { faker } from '@faker-js/faker'; // Define factory for User entity const userFactory = Factory.define<User>(() => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), })); describe('UserService', () => { let service: UserService; let repository: Repository<User>; const mockRepository = { find: jest.fn(), findOne: jest.fn(), save: jest.fn(), update: jest.fn(), delete: jest.fn(), }; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ UserService, { provide: getRepositoryToken(User), useValue: mockRepository, }, ], }).compile(); service = module.get<UserService>(UserService); repository = module.get<Repository<User>>(getRepositoryToken(User)); }); afterEach(() => { jest.clearAllMocks(); }); it('should return user when found', async () => { const mockUser = userFactory.build(); mockRepository.findOne.mockResolvedValue(mockUser); const result = await service.findById(mockUser.id); expect(result).toEqual(mockUser); }); }); ``` ### Controller Test Pattern ```typescript import { Factory } from 'fishery'; import { faker } from '@faker-js/faker'; const userFactory = Factory.define<User>(() => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), })); describe('UserController', () => { let controller: UserController; let service: UserService; const mockService = { findAll: jest.fn(), findById: jest.fn(), create: jest.fn(), }; beforeEach(async () => { const module = await Test.createTestingModule({ controllers: [UserController], providers: [{ provide: UserService, useValue: mockService }], }).compile(); controller = module.get<UserController>(UserController); }); it('should return user from service', async () => { const mockUser = userFactory.build(); mockService.findById.mockResolvedValue(mockUser); const result = await controller.findById(mockUser.id); expect(result).toEqual(mockUser); }); }); ``` ### Integration Tests (E2E) ```typescript describe('UserController (e2e)', () => { let app: INestApplication; beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = module.createNestApplication(); await app.init(); }); afterAll(async () => { await app.close(); }); it('/users (GET)', () => { return request(app.getHttpServer()) .get('/users') .expect(200) .expect((res) => { expect(Array.isArray(res.body)).toBe(true); }); }); }); ``` ## E2E Testing (Playwright) ```typescript import { Factory } from 'fishery'; import { faker } from '@faker-js/faker'; const userDataFactory = Factory.define(() => ({ name: faker.person.fullName(), email: faker.internet.email(), })); test.describe('User Management', () => { test('should create new user', async ({ page }) => { const userData = userDataFactory.build(); await page.goto('/users'); await page.click('text=Add User'); await page.fill('[name="name"]', userData.name); await page.fill('[name="email"]', userData.email); await page.click('text=Submit'); // ✅ Use proper waits await expect(page.locator(`text=${userData.name}`)).toBeVisible(); }); }); ``` ## Best Practices ### Always Use Faker for Test Data ```typescript // ✅ GOOD: Use faker const user = { id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), age: faker.number.int({ min: 18, max: 100 }), }; // ❌ BAD: Hardcoded data const user = { id: '123', name: 'Test User' }; ``` ### Use Factories Instead of Static Mocks **Use Fishery** for creating test data factories with sensible defaults and overrides. **CRITICAL — UI factories live in `redisinsight/ui/src/mocks/factories/`**, organized by domain (`<domain>/<TypeName>.factory.ts`), exported as `<TypeName>Factory`. Before writing test data in a UI spec, **check for an existing factory there and reuse it**; if none fits, **add a new factory file in that directory** rather than inlining mocks in the spec. ```typescript // ✅ GOOD: reuse the shared factory (redisinsight/ui/src/mocks/factories/database/DBInstance.factory.ts) import { DBInstanceFactory } from 'uiSrc/mocks/factories/database/DBInstance.factory'; const instance = DBInstanceFactory.build(); const cluster = DBInstanceFactory.build({ connectionType: ConnectionType.Cluster, }); const many = DBInstanceFactory.buildList(5); // ✅ GOOD: a new factory file, when none exists yet
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る