---
id: cacr92/WeReply/testing-strategy
version: "7f3e6c5b"
license: MIT
install: manual
updated: 2026-01-30
---
# testing-strategy — Testing Strategy provides comprehensive testing patterns for Tauri applications spanning Rust backend and React frontend. It covers unit and integration tests, test fixtures, command mocking, and test coverage strategies to support quality assurance and TDD workflows.
Publisher: cacr92 · Stars: 4 · Updated: 2026-01-30
Install (manual): `git clone https://github.com/cacr92/WeReply`
## SKILL.md
# Testing Strategy Skill
Comprehensive testing guidance for Tauri applications with Rust backend and React frontend.
## Overview
This skill provides testing strategies for:
- Rust unit and integration tests
- React component and hook tests
- Tauri command mocking
- Database testing with fixtures
- Test coverage and quality gates
- TDD (Test-Driven Development) workflows
## When This Skill Applies
This skill activates when:
- Writing new tests for Rust or TypeScript code
- Implementing test doubles and mocks
- Setting up test infrastructure
- Debugging test failures
- Improving test coverage
- Planning testing strategy for features
## Rust Testing
### Unit Tests
**Location**: In the same module as the code being tested
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_formula_cost_calculation() {
let materials = vec![
FormulaMaterial {
material_code: "corn".to_string(),
proportion: 50.0,
price: 2.5,
},
FormulaMaterial {
material_code: "soybean".to_string(),
proportion: 50.0,
price: 3.0,
},
];
let cost = calculate_total_cost(&materials);
assert!((cost - 2.75).abs() < 0.01); // Average of 2.5 and 3.0
}
#[test]
fn test_empty_materials() {
let materials = vec![];
let cost = calculate_total_cost(&materials);
assert_eq!(cost, 0.0);
}
#[test]
#[should_panic(expected = "Proportion cannot be negative")]
fn test_negative_proportion_panics() {
let material = FormulaMaterial {
material_code: "test".to_string(),
proportion: -10.0,
price: 1.0,
};
validate_material(&material);
}
}
```
### Integration Tests
**Location**: `tests/` directory at project root
```rust
// tests/formula_integration_tests.rs
use ca_cr_feed_formula::database::create_pool;
use ca_cr_feed_formula::formula::FormulaService;
#[tokio::test]
async fn test_create_and_retrieve_formula() {
// Use test database
let pool = create_pool(":memory:").await.expect("Failed to create pool");
let service = FormulaService::new(pool);
// Create formula
let create_dto = CreateFormulaDto {
name: "Test Formula".to_string(),
species_code: "pig".to_string(),
description: None,
};
let formula_id = service.create_formula(create_dto)
.await
.expect("Failed to create formula");
// Retrieve formula
let formula = service.get_formula(formula_id)
.await
.expect("Failed to retrieve formula");
assert_eq!(formula.name, "Test Formula");
assert_eq!(formula.species_code, "pig");
}
#[tokio::test]
async fn test_formula_not_found() {
let pool = create_pool(":memory:").await.expect("Failed to create pool");
let service = FormulaService::new(pool);
let result = service.get_formula(99999).await;
assert!(result.is_err());
}
```
### Database Fixtures
```rust
pub struct TestFixture {
pub pool: SqlitePool,
pub material_service: MaterialService,
pub formula_service: FormulaService,
}
impl TestFixture {
pub async fn setup() -> Self {
let pool = create_pool(":memory:").await.unwrap();
// Run migrations
sqlx::migrate!("./migrations")
.run(&pool)
.await
.unwrap();
// Insert test data
Self::seed_test_data(&pool).await;
Self {
pool: pool.clone(),
material_service: MaterialService::new(pool.clone()),
formula_service: FormulaService::new(pool),
}
}
async fn seed_test_data(pool: &SqlitePool) {
// Insert test materials
sqlx::query!(
"INSERT INTO materials (code, name, price, category) VALUES (?, ?, ?, ?)",
"corn", "Corn", 2.5, "energy"
)
.execute(pool)
.await
.unwrap();
sqlx::query!(
"INSERT INTO materials (code, name, price, category) VALUES (?, ?, ?, ?)",
"soybean", "Soybean Meal", 3.0, "protein"
)
.execute(pool)
.await
.unwrap();
}
pub async fn teardown(self) {
self.pool.close().await;
}
}
#[tokio::test]
async fn test_with_fixture() {
let fixture = TestFixture::setup().await;
let corn = fixture.material_service.get_material("corn")
.await
.unwrap();
assert_eq!(corn.name, "Corn");
fixture.teardown().await;
}
```
## React Testing
### Component Tests
```typescript
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FormulaForm } from './FormulaForm';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { message } from 'antd';
// Mock Tauri commands
vi.mock('../bindings', () => ({
commands: {
createFormula: vi.fn(),
updateFormula: vi.fn(),
},
}));
// Mock message
vi.mock('antd', async () => ({
...((await vi.importActual('antd')) as any),
message: {
success: vi.fn(),
error: vi.fn(),
},
}));
describe('FormulaForm', () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
const wrapper = ({ children }: { children: React.ReactNode }) => (
{children}
);
beforeEach(() => {
vi.clearAllMocks();
});
it('should render form fields', () => {
render(, { wrapper });
expect(screen.getByLabelText(/配方名称/)).toBeInTheDocument();
expect(screen.getByLabelText(/品种代码/)).toBeInTheDocument();
});
it('should show validation errors for empty name', async () => {
const user = userEvent.setup();
render(, { wrapper });
const submitButton = screen.getByText(/保存/);
await user.click(submitButton);
await waitFor(() => {
expect(screen.getByText(/请输入配方名称/)).toBeInTheDocument();
});
});
it('should submit form with valid data', async () => {
const mockCreate = vi.mocked(commands.createFormula);
mockCreate.mockResolvedValueOnce({
success: true,
data: { id: 1, name: '测试配方' },
});
const user = userEvent.setup();
render(, { wrapper });
await user.type(screen.getByLabelText(/配方名称/), '测试配方');
await user.click(screen.getByText(/保存/));
await waitFor(() => {
expect(mockCreate).toHaveBeenCalledWith({
name: '测试配方',
species_code: 'pig',
});
expect(message.success).toHaveBeenCalledWith('保存成功');
});
});
it('should handle API errors', async () => {
const mockCreate = vi.mocked(commands.createFormula);
mockCreate.mockResolvedValueOnce({
success: false,
message: '创建失败:配方已存在',
});
const user = userEvent.setup();
render(, { wrapper });
await user.type(screen.getByLabelText(/配方名称/), '测试配方');
await user.click(screen.getByText(/保存/));
await waitFor(() => {
expect(message.error).toHaveBeenCalledWith('创建失败:配方已存在');
});
});
});
```
### Hook Tests
```typescript
import { renderHook, act, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useMaterials } from './useMaterials';
vi.mock('../bindings', () => ({
commands: {
getMaterials: vi.fn(),
},
}));
describe('useMaterials', () => {
let queryClient: QueryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
},
});
});
it('should fetch materials successfully', async () => {
const mockData = [
{ code: 'corn', name: 'Corn', price: 2.5 },
{ code: 'soybean', name: 'Soybean', price: 3.0 },
];
vi.mocked(commands.getMaterials).mockResolvedValueOnce({
success: true,
data: mockData,
});
const { result } = renderHook(() => useMaterials(), {
wrapper: ({ children }) => (
{children}
),
});
expect(result.current.isLoading).toBe(true);
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.materials).toEqual(mockData);
});
});
it('should handle fetch errors', async () => {
vi.mocked(commands.getMaterials).mockResolvedValueOnce({
success: false,
message: 'Failed to load materials',
});
const { result } = renderHook(() => useMaterials(), {
wrapper: ({ children }) => (
{children}
),
});
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.materials).toBeNull();
});
});
});
```
## TDD Workflow
### Red-Green-Refactor Cycle
```bash
# 1. RED: Write failing test
# 2. GREEN: Write minimum code to pass
# 3. REFACTOR: Improve code while tests pass
```
**Example: TDD for Nutrition Calculation**
```rust
// Step 1: Write failing test (RED)
#[test]
fn test_calculate_protein_content() {
let materials = vec![
create_material("corn", 8.5), // 8.5% protein
create_material("soybean", 44.0), // 44% protein
];
let proportions = vec![50.0, 50.0]; // 50% each
let protein = calculate_nutrient(&materials, &proportions, "protein");
assert!((protein - 26.25).abs() < 0.01); // Weighted average
}
// Step 2: Write minimum implementation (GREEN)
fn calculate_nutrient(
materials: &[Material],
proportions: &[f64],
nutrient_code: &str,
) -> f64 {
materials
.iter()
.zip(proportions.iter())
.map(|(m, prop)| {
let value = m.nutrients.get(nutrient_code).copied().unwrap_or(0.0);
value * (prop / 100.0)
})
.sum()
}
// Step 3: Refactor (REFACTOR)
// Improve code structure, add validation, optimize performance
```
## Test Coverage
### Setting Up Coverage
**Rust (tarpaulin):**
```bash
cargo install tarpaulin
# Generate coverage report
cargo tarpaulin --out Html --output-dir coverage
# View report
open coverage/index.html
```
**TypeScript (vitest):**
```bash
# Add to package.json
{
"scripts": {
"test": "vitest",
"test:coverage": "vitest --coverage"
}
}
# Run coverage
npm run test:coverage
```
### Coverage Goals
| Component Type | Target Coverage |
|----------------|-----------------|
| Business logic (Rust) | > 90% |
| UI Components | > 80% |
| Utilities/Helpers | > 95% |
| Integration tests | Key workflows only |
## Testing Best Practices
### DO's ✅
- Write tests before implementation (TDD)
- Use descriptive test names (`test_calculate_cost_with_discount`)
- Test edge cases and error conditions
- Mock external dependencies (database, API)
- Keep tests independent and isolated
- Use fixtures for complex test data
- Test behavior, not implementation
### DON'Ts ❌
- Don't test trivial getters/setters
- Don't write tests that are tightly coupled to implementation
- Don't use shared state between tests
- Don't ignore failing tests
- Don't mock code you own (only external deps)
- Don't write tests that are too complex to understand
## Quick Reference
### Test Structure Template
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_descriptive_name() {
// Arrange
let input = setup_test_data();
// Act
let result = function_under_test(input);
// Assert
assert_eq!(result, expected);
}
}
```
### React Test Template
```typescript
describe('ComponentName', () => {
it('should do something when condition', () => {
// Arrange
render();
// Act
fireEvent.click(screen.getByText('Button'));
// Assert
expect(screen.getByText('Result')).toBeInTheDocument();
});
});
```
### Common Test Commands
```bash
# Rust tests
cargo test # Run all tests
cargo test test_name # Run specific test
cargo test -- --nocapture # Show print output
cargo nextest run # Faster test runner
# TypeScript tests
npm test # Run all tests
npm test -- FormulaForm # Run specific file
npm run test:coverage # With coverage
```
## When to Use This Skill
Activate this skill when:
- Writing new tests for Rust or TypeScript
- Setting up test infrastructure
- Debugging test failures
- Improving test coverage
- Planning testing strategy
- Implementing TDD workflow
- Creating mocks and fixtures
[View on SkillFed](https://skillfed.io/cacr92/WeReply/testing-strategy) · [View on GitHub](https://github.com/cacr92/WeReply)