Basic Testing
Testing Translation Functionality
import i18next from 'i18next';
describe('i18next translations', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome',
greeting: 'Hello {{name}}'
}
}
}
});
});
test('should translate simple key', () => {
expect(i18next.t('welcome')).toBe('Welcome');
});
test('should translate with interpolation', () => {
expect(i18next.t('greeting', { name: 'John' })).toBe('Hello John');
});
});
React Component Testing
Testing Components Using useTranslation
import { render, screen } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function Welcome() {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}
describe('Welcome component', () => {
const renderWithI18n = (component) => {
return render(
<I18nextProvider i18n={i18next}>
{component}
</I18nextProvider>
);
};
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome'
}
}
}
});
});
test('should display welcome message', () => {
renderWithI18n(<Welcome />);
expect(screen.getByText('Welcome')).toBeInTheDocument();
});
});
Testing Language Switching
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function LanguageSwitcher() {
const { t, i18n } = useTranslation();
return (
<div>
<p>{t('currentLang')}</p>
<button onClick={() => i18n.changeLanguage('zh')}>中文</button>
<button onClick={() => i18n.changeLanguage('en')}>English</button>
</div>
);
}
describe('LanguageSwitcher', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
currentLang: 'English'
}
},
zh: {
translation: {
currentLang: '中文'
}
}
}
});
});
test('should switch language', async () => {
render(
<I18nextProvider i18n={i18next}>
<LanguageSwitcher />
</I18nextProvider>
);
expect(screen.getByText('English')).toBeInTheDocument();
fireEvent.click(screen.getByText('中文'));
await waitFor(() => {
expect(screen.getByText('中文')).toBeInTheDocument();
});
});
});
Testing Trans Component
import { render, screen } from '@testing-library/react';
import { Trans } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function MyComponent() {
return (
<Trans i18nKey="user.greeting">
Welcome <strong>{{name}}</strong>
</Trans>
);
}
describe('Trans component', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
'user.greeting': 'Welcome <1>{{name}}</1>'
}
}
}
});
});
test('should render translated content with interpolation', () => {
render(
<I18nextProvider i18n={i18next}>
<MyComponent />
</I18nextProvider>
);
expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
});
});
Mocking i18next
Create Test Utility Functions
// test-utils/i18n.js
import { initReactI18next } from 'react-i18next';
import i18next from 'i18next';
export const createI18nInstance = (resources = {}) => {
const instance = i18next.createInstance();
instance.use(initReactI18next).init({
lng: 'en',
resources: {
en: {
translation: resources
}
}
});
return instance;
};
export const renderWithI18n = (component, resources = {}) => {
const i18n = createI18nInstance(resources);
return render(
<I18nextProvider i18n={i18n}>
{component}
</I18nextProvider>
);
};
Using Test Utilities
import { renderWithI18n } from './test-utils/i18n';
test('should render with mocked translations', () => {
renderWithI18n(<MyComponent />, {
welcome: 'Welcome',
goodbye: 'Goodbye'
});
expect(screen.getByText('Welcome')).toBeInTheDocument();
});
Testing Lazy Loading
import { render, screen, waitFor } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function LazyComponent() {
const { t, ready } = useTranslation('lazy', { useSuspense: false });
if (!ready) {
return <div>Loading...</div>;
}
return <p>{t('content')}</p>;
}
describe('Lazy loading', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {}
}
}
});
});
test('should show loading state initially', () => {
render(
<I18nextProvider i18n={i18next}>
<LazyComponent />
</I18nextProvider>
);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
test('should load namespace and show content', async () => {
render(
<I18nextProvider i18n={i18next}>
<LazyComponent />
</I18nextProvider>
);
i18next.addResourceBundle('en', 'lazy', { content: 'Loaded content' });
await waitFor(() => {
expect(screen.getByText('Loaded content')).toBeInTheDocument();
});
});
});
Testing Missing Translations
describe('Missing translations', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
fallbackLng: 'en',
saveMissing: true,
missingKeyHandler: (lng, ns, key) => {
console.warn(`Missing translation: ${lng}.${ns}.${key}`);
},
resources: {
en: {
translation: {
existing: 'Existing translation'
}
}
}
});
});
test('should return key when translation is missing', () => {
const result = i18next.t('nonexistent');
expect(result).toBe('nonexistent');
});
test('should use fallback translation', () => {
i18next.addResourceBundle('en', 'translation', { fallback: 'Fallback' });
const result = i18next.t('fallback');
expect(result).toBe('Fallback');
});
});
Integration Testing
describe('i18next integration tests', () => {
test('should handle language change across components', async () => {
const { rerender } = render(
<I18nextProvider i18n={i18next}>
<App />
</I18nextProvider>
);
expect(screen.getByText('English')).toBeInTheDocument();
await i18next.changeLanguage('zh');
rerender(
<I18nextProvider i18n={i18next}>
<App />
</I18nextProvider>
);
expect(screen.getByText('中文')).toBeInTheDocument();
});
});
Best Practices
- Isolated Tests: Each test should run independently without depending on other tests
- Mock Resources: Use mocked translation resources to avoid depending on actual files
- Test Utilities: Create reusable test utility functions
- Async Testing: Properly handle async operations like language switching and lazy loading
- Edge Cases: Test edge cases like missing translations and error conditions
- Snapshot Testing: Use snapshot testing for translated components to ensure consistency
- Performance Testing: Test performance when dealing with large amounts of translations