Using AI to Write Your First Tests

Introduction
As a developer primarily working with PHP, TypeScript, and Laravel, diving into the world of JavaScript testing might seem daunting. However, when faced with package manager issues while building Syndeos—a desktop application for VPS management—I discovered that writing tests wasn't just about catching bugs; it was about ensuring package compatibility and creating a more robust development workflow.
This article chronicles my journey of using AI assistance to write my first tests, specifically focused on catching package incompatibility issues that arose when switching from npm to pnpm.
The Problem: Package Manager Compatibility
While developing Syndeos with Tauri v2, React 19, and various modern libraries, I encountered a frustrating issue. When attempting to switch from npm to pnpm for better performance and disk space efficiency, I discovered that npm's stricter dependency resolution had been masking compatibility issues between packages.
The specific issues were:
react-day-picker
version conflicts withdate-fns
- Certain peer dependency mismatches that pnpm's more lenient hoisting exposed
Rather than staying locked into npm or manually checking compatibility for every update, I decided to write automated tests—something I'd never done before in the JavaScript ecosystem.
Why Tests for Package Compatibility?
Traditional testing focuses on functionality, but package compatibility tests serve a different purpose:
- Early Detection: Catch incompatibility issues before they break the build
- Package Manager Agnostic: Ensure the codebase works regardless of the package manager
- Documentation: Tests serve as living documentation of package requirements
- CI/CD Integration: Automated checks in the build pipeline prevent breaking changes
Writing My First Compatibility Test with AI Assistance
Step 1: Understanding the Test Structure
Coming from a Laravel background where testing follows clear MVC patterns, I needed to understand JavaScript testing conventions. With AI assistance, I learned that compatibility tests should:
- Import the problematic packages together
- Attempt to use them in ways that would expose incompatibility
- Verify that the expected APIs are available and functional
Step 2: The First Test File
Here's the test I wrote with AI guidance for checking react-day-picker
and date-fns
compatibility:
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { DayPicker } from 'react-day-picker';
import * as dateFns from 'date-fns';
describe('react-day-picker and date-fns compatibility', () => {
it('DayPicker renders without crashing', () => {
render(<DayPicker />);
});
it('DayPicker formats dates correctly with date-fns', () => {
const today = new Date();
render(
<DayPicker
month={today}
formatters={{
formatMonthCaption: (date) => dateFns.format(date, 'MMMM yyyy'),
formatWeekdayName: (date) => dateFns.format(date, 'EEE'),
}}
/>
);
const monthCaption = dateFns.format(today, 'MMMM yyyy');
expect(screen.getByText(monthCaption)).toBeInTheDocument();
});
it('renders with correct month and allows interactions', async () => {
const onSelectSpy = vi.fn();
const today = new Date();
const currentMonth = dateFns.format(today, 'MMMM yyyy');
const { container } = render(
<DayPicker
mode="single"
selected={today}
onSelect={onSelectSpy}
defaultMonth={today}
/>
);
expect(screen.getByText(currentMonth)).toBeInTheDocument();
const dayCells = container.querySelectorAll('.rdp-day');
expect(dayCells.length).toBeGreaterThan(0);
const dayToClick = Array.from(dayCells).find(cell =>
cell.getAttribute('aria-disabled') !== 'true' &&
!cell.classList.contains('rdp-day_outside')
);
if (dayToClick) {
await userEvent.click(dayToClick);
expect(onSelectSpy).toHaveBeenCalled();
}
});
it('DayPicker uses complex date-fns features correctly', () => {
const startDate = dateFns.startOfWeek(new Date());
const endDate = dateFns.endOfWeek(new Date());
render(
<DayPicker
mode="range"
defaultMonth={startDate}
selected={{
from: startDate,
to: endDate
}}
/>
);
});
});
Step 3: Integrating Tests into the Build Process
The real power comes from integrating these tests into the build pipeline. I modified the package.json
to run tests before building:
{
"scripts": {
"build": "tsc && vitest run && vite build",
"test": "vitest run",
"test:watch": "vitest"
}
}
This ensures that:
- TypeScript compilation happens first (
tsc
) - Tests run to verify compatibility (
vitest run
) - Only if tests pass does the build continue (
vite build
)
Key Learnings and Best Practices
1. Start Simple
My first test simply verified that components could render. This caught the most basic incompatibility issues.
2. Test Real-World Usage
Don't just import packages—use them together in ways that mirror your actual application code.
3. Use AI as a Learning Tool
AI helped me understand:
- Testing library conventions
- How to structure compatibility tests
- Best practices for async testing
- The difference between unit and integration tests
4. Focus on Integration Points
The most valuable tests checked where packages interact:
- Date formatting with external libraries
- Event handling across package boundaries
- Shared type definitions
5. Document Test Purpose
Each test includes a clear description of what compatibility issue it's checking for.
Benefits Beyond Compatibility
Writing these tests provided unexpected benefits:
- Confidence in Updates: I can now update packages knowing tests will catch breaking changes
- Better Understanding: Writing tests forced me to understand how packages actually work together
- Living Documentation: Tests serve as examples of proper package usage
- Team Onboarding: New developers can understand package relationships through tests
Applying MVC Thinking to JavaScript Testing
Coming from Laravel's MVC architecture, I found it helpful to think of tests in similar terms:
- Model Tests: Verify data structures and transformations work across packages
- View Tests: Ensure UI components from different packages render together
- Controller Tests: Check that event handlers and state management integrate properly
Practical Tips for First-Time Test Writers
- Use AI to Bootstrap: Don't be afraid to ask AI for test structure examples
- Start with Happy Paths: Test the normal use cases before edge cases
- Run Tests Frequently: Use
test:watch
during development - Test One Thing: Each test should verify a single compatibility aspect
- Mock Sparingly: For compatibility tests, use real implementations when possible
Conclusion
Writing my first JavaScript tests to solve package compatibility issues taught me that testing isn't just about catching bugs—it's about ensuring the entire ecosystem of packages works harmoniously together. By integrating these tests into the build process, I've created a safety net that allows me to use pnpm's benefits while ensuring compatibility that npm's strict resolution previously enforced.
The journey from encountering package compatibility errors to having automated tests that prevent such issues has been transformative. It's shown me that with the right approach and tools (including AI assistance), even developers new to testing can create valuable test suites that solve real problems.
For fellow developers facing similar package manager transitions or compatibility concerns, I encourage you to write that first test. Start simple, use AI as a learning companion, and focus on the specific problems you're trying to solve. The investment in learning testing will pay dividends in confidence, code quality, and development speed.
Remember: every expert was once a beginner. Your first test doesn't need to be perfect—it just needs to solve a real problem. In my case, that problem was package compatibility, and the solution has made Syndeos a more robust and maintainable project.