Тема
Тестирование Module Federation
До тестов подготовьте MF bootstrap, разберите participant scope и реализуйте host/remote contract.
Уровни проверки
| Уровень | Что проверяет |
|---|---|
| TypeScript contract | Совместимость bootstrap и API adapter. |
| Unit | Business store без React и remote loader. |
| Integration | Runtime, scopes, cache и namespace policy. |
| Browser E2E | Реальный mount, HTTP, unmount и два remote. |
Compatibility до mount
ts
expect(() => {
assertQueryRuntimeCompatibility(runtime.getProtocolDescriptor(), {
protocolVersion: QUERY_RUNTIME_PROTOCOL_VERSION,
minimumRuntimeVersion: "3.2.0",
requiredCapabilities: ["query", "mutation"],
})
}).not.toThrow()Добавьте отрицательные тесты для protocol mismatch, слишком старого runtime и отсутствующей capability. Remote UI не должен монтироваться после отказа.
Namespace denial
ts
const scope = runtime.createParticipantScope({
participantId: "support-remote",
participantType: "remote",
capabilities: {
queryNamespaces: [["tickets"]],
mutationNamespaces: ["tickets"],
},
})
expect(() => scope.query.getData(["invoices", "list"])).toThrow()
expect(backendMetrics.invoiceRequests).toBe(0)Главное утверждение: отказ происходит до network.
Private isolation двух mount
ts
const first = runtime.createParticipantScope(registration)
const second = runtime.createParticipantScope(registration)
expect(first.identity.participantId).toBe(second.identity.participantId)
expect(first.identity.instanceId).not.toBe(second.identity.instanceId)Запишите разные данные в одинаковый logical key и убедитесь, что каждый mount видит только свой snapshot.
Shared query и deduplication
Оба participant должны получить разрешённый shared namespace и стабильный definitionId:
ts
const createDictionaryStore = (scope: IScopedQueryRuntime) =>
scope.query.createStore<Dictionary>({
definitionId: "shared.dictionary.v1",
queryKey: () => ["shared", "dictionary"],
queryFn: ({ signal }) => dictionaryApi.load({ signal }),
})Запустите два stores одновременно и проверьте один backend request. Затем создайте store с тем же key и другим definitionId: cache должен выбросить QueryDefinitionConflictError до второго backend request.
Unmount и диагностика
ts
const before = runtime.getDiagnostics()
unmountRemote()
scope.dispose()
const after = runtime.getDiagnostics()
expect(after.activeParticipantCount).toBe(before.activeParticipantCount - 1)Также проверьте отмену owned direct request и отсутствие отмены shared request, который всё ещё нужен другому participant.
Playwright стенд проекта
В playwright/ уже есть browser-сценарии:
- два QueryStore дедуплицируют HTTP;
- Mutation обновляет активный Query;
- InfiniteQueryStore загружает следующую страницу;
- FetchStore выполняет auth refresh;
- RequestStore отменяет slow request;
- scope запрещает чужой namespace;
- React frontend вызывает API через business stores.
Запуск:
bash
npm run playwright:install
npm run playwright:testAPI-проверки без запуска Chromium:
bash
npm run playwright:test:api