乐闻世界logo
搜索文章和话题

How to test Module Federation? What are the testing strategies?

2026年2月19日 17:45

Testing strategies for Module Federation need to consider module independence and dependency relationships. Here is a complete testing solution:

1. Unit Testing

Testing Remote Modules:

javascript
// Button.test.js import { render, screen } from '@testing-library/react' import Button from './Button' describe('Button Component', () => { it('renders button with correct text', () => { render(<Button label="Click me" />) expect(screen.getByText('Click me')).toBeInTheDocument() }) it('calls onClick handler when clicked', () => { const handleClick = jest.fn() render(<Button label="Click me" onClick={handleClick} />) screen.getByText('Click me').click() expect(handleClick).toHaveBeenCalledTimes(1) }) })

Testing Shared Dependencies:

javascript
// sharedDependency.test.js import React from 'react' describe('Shared React Dependency', () => { it('uses singleton instance', () => { const react1 = require('react') const react2 = require('react') expect(react1).toBe(react2) }) it('maintains correct version', () => { const react = require('react') expect(react.version).toMatch(/^17\./) }) })

2. Integration Testing

Testing Module Loading:

javascript
// moduleLoading.test.js import { render, screen, waitFor } from '@testing-library/react' describe('Remote Module Loading', () => { it('loads remote component successfully', async () => { const RemoteComponent = React.lazy(() => import('remoteApp/Button') ) render( <React.Suspense fallback={<div>Loading...</div>}> <RemoteComponent label="Remote Button" /> </React.Suspense> ) await waitFor(() => { expect(screen.getByText('Remote Button')).toBeInTheDocument() }) }) it('handles loading errors gracefully', async () => { const FallbackComponent = () => <div>Fallback</div> const RemoteComponent = React.lazy(() => import('remoteApp/NonExistent') .catch(() => import('./Fallback')) ) render( <React.Suspense fallback={<div>Loading...</div>}> <RemoteComponent /> </React.Suspense> ) await waitFor(() => { expect(screen.getByText('Fallback')).toBeInTheDocument() }) }) })

Testing Inter-Module Communication:

javascript
// interModuleCommunication.test.js describe('Inter-Module Communication', () => { it('shares state between modules', async () => { const { createStore } = await import('remoteApp/store') const store = createStore() store.dispatch({ type: 'SET_VALUE', payload: 'test' }) expect(store.getState().value).toBe('test') }) it('emits events across modules', async () => { const { eventBus } = await import('remoteApp/eventBus') const handler = jest.fn() eventBus.on('test-event', handler) eventBus.emit('test-event', { data: 'test' }) expect(handler).toHaveBeenCalledWith({ data: 'test' }) }) })

3. End-to-End Testing

Testing with Cypress:

javascript
// cypress/integration/app.spec.js describe('Module Federation E2E', () => { it('loads remote module on page load', () => { cy.visit('/') cy.get('[data-testid="remote-button"]').should('be.visible') cy.get('[data-testid="remote-button"]').should('contain', 'Remote Button') }) it('interacts with remote component', () => { cy.visit('/') cy.get('[data-testid="remote-button"]').click() cy.get('[data-testid="message"]').should('contain', 'Button clicked') }) it('handles module loading failure', () => { cy.intercept('GET', '**/remoteEntry.js', { forceNetworkError: true }) cy.visit('/') cy.get('[data-testid="fallback-message"]').should('be.visible') }) })

Testing with Playwright:

javascript
// app.spec.ts import { test, expect } from '@playwright/test' test.describe('Module Federation E2E', () => { test('loads remote module successfully', async ({ page }) => { await page.goto('/') const button = page.locator('[data-testid="remote-button"]') await expect(button).toBeVisible() await expect(button).toHaveText('Remote Button') }) test('handles remote module interactions', async ({ page }) => { await page.goto('/') await page.click('[data-testid="remote-button"]') const message = page.locator('[data-testid="message"]') await expect(message).toContainText('Button clicked') }) })

4. Performance Testing

Module Loading Performance Test:

javascript
// performance.test.js describe('Module Loading Performance', () => { it('loads remote module within acceptable time', async () => { const startTime = performance.now() await import('remoteApp/Button') const loadTime = performance.now() - startTime expect(loadTime).toBeLessThan(2000) // Load within 2 seconds }) it('measures bundle size', () => { const stats = require('./webpack-stats.json') const remoteEntrySize = stats.assets.find( asset => asset.name === 'remoteEntry.js' ).size expect(remoteEntrySize).toBeLessThan(100 * 1024) // Less than 100KB }) })

5. Mocking Remote Modules

Using Jest Mock:

javascript
// __mocks__/remoteApp/Button.js export default function MockButton({ label }) { return <button data-testid="mock-button">{label}</button> } // test file jest.mock('remoteApp/Button') import Button from 'remoteApp/Button' describe('Mocked Remote Module', () => { it('uses mocked component', () => { render(<Button label="Mocked Button" />) expect(screen.getByTestId('mock-button')).toBeInTheDocument() }) })

Using MSW to Intercept Requests:

javascript
// msw.js import { setupServer } from 'msw/node' import { rest } from 'msw' const server = setupServer( rest.get('http://localhost:3001/remoteEntry.js', (req, res, ctx) => { return res( ctx.set('Content-Type', 'application/javascript'), ctx.body(` var remoteApp; (function() { remoteApp = { get: function(module) { return import('./' + module + '.js'); }, init: function(sharedScope) { // initialization logic } }; })(); `) ) }) ) beforeAll(() => server.listen()) afterEach(() => server.resetHandlers()) afterAll(() => server.close())

6. Test Configuration

Jest Configuration:

javascript
// jest.config.js module.exports = { preset: 'ts-jest', testEnvironment: 'jsdom', moduleNameMapper: { '\\.(css|less|scss|sass)$': 'identity-obj-proxy', '^@/(.*)$': '<rootDir>/src/$1', '^remoteApp/(.*)$': '<rootDir>/__mocks__/remoteApp/$1' }, transform: { '^.+\\.(ts|tsx|js|jsx)$': 'ts-jest' }, setupFilesAfterEnv: ['<rootDir>/jest.setup.js'] }

Cypress Configuration:

javascript
// cypress.config.js const { defineConfig } = require('cypress') module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', supportFile: 'cypress/support/e2e.js', specPattern: 'cypress/integration/**/*.cy.{js,jsx,ts,tsx}', viewportWidth: 1280, viewportHeight: 720 } })

7. Testing Best Practices

  1. Isolated Tests: Each test runs independently without depending on other tests
  2. Mock External Dependencies: Use mocks to isolate external dependencies
  3. Cover Edge Cases: Test success, failure, timeout scenarios
  4. Performance Benchmarks: Set performance baselines and monitor performance degradation
  5. Continuous Integration: Integrate tests in CI/CD
  6. Test Coverage: Maintain high test coverage (>80%)
  7. Documentation: Add comments and documentation for complex tests

Through these testing strategies, the quality and stability of Module Federation applications can be ensured.

标签:Module Federation