Testing Basics
What testing framework does Sonamu use?
What testing framework does Sonamu use?
Sonamu uses Vitest.Features:
- Very fast execution based on Vite
- Jest-compatible API
- Native TypeScript support
- HMR support
How do I run tests?
How do I run tests?
Copy
# Run all tests
pnpm test
# Run specific file only
pnpm test user.model.test.ts
# Watch mode (detect file changes)
pnpm test --watch
# Coverage report
pnpm test --coverage
# UI mode
pnpm test --ui
How do I create test files?
How do I create test files?
Generate with Scaffolding:
- Access Sonamu UI
- Scaffolding tab
- Select Entity
- Template: Select
model_test - Click Generate
Copy
// user.model.test.ts
import { beforeAll, describe, expect, test } from "vitest";
import { UserModel } from "./user.model";
beforeAll(async () => {
await UserModel.setTestData();
});
describe("UserModel", () => {
test("findById", async () => {
const user = await UserModel.findById(1);
expect(user).toBeDefined();
expect(user.id).toBe(1);
});
test("save", async () => {
const user = await UserModel.save({
name: "Test User",
email: "test@example.com"
});
expect(user.id).toBeGreaterThan(0);
expect(user.name).toBe("Test User");
});
});
Test Data
How do I prepare test data?
How do I prepare test data?
setTestData() method:Using in tests:
Copy
// user.model.ts
class UserModelClass extends BaseModelClass {
static async setTestData() {
await this.truncate();
await this.bulkInsert([
{ id: 1, name: "John", email: "john@example.com" },
{ id: 2, name: "Jane", email: "jane@example.com" },
{ id: 3, name: "Bob", email: "bob@example.com" }
]);
}
}
Copy
import { beforeAll, describe, test } from "vitest";
beforeAll(async () => {
await UserModel.setTestData();
await PostModel.setTestData();
await CommentModel.setTestData();
});
describe("UserModel", () => {
test("findById", async () => {
const user = await UserModel.findById(1);
expect(user.name).toBe("John");
});
});
How do I reset DB for each test?
How do I reset DB for each test?
Using beforeEach:
Copy
import { beforeEach, describe, test } from "vitest";
beforeEach(async () => {
await UserModel.setTestData();
});
describe("UserModel", () => {
test("create user", async () => {
const user = await UserModel.save({ name: "New User" });
expect(user.id).toBeGreaterThan(3);
});
test("delete user", async () => {
await UserModel.del(1);
const user = await UserModel.findById(1);
expect(user).toBeNull();
});
});
Model Testing
How do I test CRUD methods?
How do I test CRUD methods?
Copy
describe("UserModel CRUD", () => {
test("findById", async () => {
const user = await UserModel.findById(1);
expect(user).toBeDefined();
expect(user.id).toBe(1);
});
test("findMany", async () => {
const result = await UserModel.findMany("A", { num: 10, page: 1 });
expect(result.rows.length).toBeGreaterThan(0);
expect(result.total).toBeGreaterThan(0);
});
test("save - create", async () => {
const user = await UserModel.save({
name: "New User",
email: "new@example.com"
});
expect(user.id).toBeGreaterThan(0);
expect(user.name).toBe("New User");
});
test("save - update", async () => {
const user = await UserModel.save({
id: 1,
name: "Updated Name"
});
expect(user.id).toBe(1);
expect(user.name).toBe("Updated Name");
});
test("del", async () => {
await UserModel.del(1);
const user = await UserModel.findById(1);
expect(user).toBeNull();
});
});
How do I test Subsets?
How do I test Subsets?
Copy
describe("UserModel Subsets", () => {
test("Subset A", async () => {
const result = await UserModel.findMany("A", { num: 10, page: 1 });
expect(result.rows[0]).toHaveProperty("id");
expect(result.rows[0]).toHaveProperty("name");
});
test("Subset WithPosts", async () => {
const result = await UserModel.findMany("WithPosts", { num: 10, page: 1 });
expect(result.rows[0]).toHaveProperty("posts");
expect(Array.isArray(result.rows[0].posts)).toBe(true);
});
test("Subset field existence check", async () => {
const user = await UserModel.findById(1, "WithProfile");
expect(user).toHaveProperty("profile");
expect(user.profile).toBeDefined();
});
});
How do I test Relations?
How do I test Relations?
Copy
describe("User Relations", () => {
test("User -> Posts (HasMany)", async () => {
const user = await UserModel.findById(1, "WithPosts");
expect(user.posts).toBeDefined();
expect(Array.isArray(user.posts)).toBe(true);
expect(user.posts.length).toBeGreaterThan(0);
});
test("Post -> User (BelongsToOne)", async () => {
const post = await PostModel.findById(1, "WithAuthor");
expect(post.author).toBeDefined();
expect(post.author.id).toBe(post.user_id);
});
test("User -> Profile (OneToOne)", async () => {
const user = await UserModel.findById(1, "WithProfile");
expect(user.profile).toBeDefined();
expect(user.profile.user_id).toBe(user.id);
});
});
API Testing
How do I test API methods?
How do I test API methods?
Copy
describe("User API", () => {
test("listUsers", async () => {
const result = await UserModel.listUsers({ num: 10, page: 1 });
expect(result.rows.length).toBeLessThanOrEqual(10);
expect(result.total).toBeGreaterThan(0);
});
test("createUser", async () => {
const user = await UserModel.createUser({
name: "API Test User",
email: "apitest@example.com"
});
expect(user.id).toBeGreaterThan(0);
expect(user.name).toBe("API Test User");
});
test("updateUser", async () => {
const user = await UserModel.updateUser(1, {
name: "Updated via API"
});
expect(user.id).toBe(1);
expect(user.name).toBe("Updated via API");
});
test("deleteUser", async () => {
await UserModel.deleteUser(1);
const user = await UserModel.findById(1);
expect(user).toBeNull();
});
});
How do I test errors?
How do I test errors?
Copy
import { BadRequestError, NotFoundError } from "sonamu";
describe("User API Errors", () => {
test("createUser - duplicate email", async () => {
await expect(
UserModel.createUser({
name: "Test",
email: "john@example.com" // Already exists
})
).rejects.toThrow(BadRequestError);
});
test("findById - non-existent ID", async () => {
await expect(
UserModel.findById(99999)
).rejects.toThrow(NotFoundError);
});
test("deleteUser - no permission", async () => {
await expect(
UserModel.deleteUser(1) // Permission check fails
).rejects.toThrow(ForbiddenError);
});
});
Advanced Testing
How do I test transactions?
How do I test transactions?
Copy
describe("Transactions", () => {
test("transaction success", async () => {
await UserModel.transaction(async (trx) => {
const user = await UserModel.save(
{ name: "Tx User" },
{ trx }
);
const profile = await ProfileModel.save(
{ user_id: user.id, bio: "Bio" },
{ trx }
);
expect(user.id).toBeGreaterThan(0);
expect(profile.user_id).toBe(user.id);
});
});
test("transaction rollback", async () => {
const initialCount = await UserModel.count();
try {
await UserModel.transaction(async (trx) => {
await UserModel.save({ name: "Rollback User" }, { trx });
throw new Error("Forced error");
});
} catch (error) {
// Ignore error
}
const finalCount = await UserModel.count();
expect(finalCount).toBe(initialCount); // No change
});
});
How do I test async operations?
How do I test async operations?
Copy
describe("Async Operations", () => {
test("email sending", async () => {
const result = await UserModel.sendWelcomeEmail(1);
expect(result.success).toBe(true);
});
test("file upload", async () => {
const file = {
buffer: Buffer.from("test content"),
originalname: "test.txt"
} as Express.Multer.File;
const result = await UserModel.uploadAvatar(file);
expect(result.url).toBeDefined();
expect(result.url).toContain("avatar");
});
test("external API call", async () => {
const result = await UserModel.fetchExternalData();
expect(result).toBeDefined();
});
});
How do I use Mocks?
How do I use Mocks?
Copy
import { vi } from "vitest";
describe("Mocking", () => {
test("external service Mock", async () => {
// Mock EmailService.send
const sendMock = vi.spyOn(EmailService, "send")
.mockResolvedValue({ success: true });
await UserModel.sendWelcomeEmail(1);
expect(sendMock).toHaveBeenCalledTimes(1);
expect(sendMock).toHaveBeenCalledWith(
expect.objectContaining({
to: "john@example.com",
subject: "Welcome!"
})
);
sendMock.mockRestore();
});
test("Date.now Mock", async () => {
const mockDate = new Date("2025-01-01");
vi.setSystemTime(mockDate);
const user = await UserModel.save({ name: "Test" });
expect(user.created_at).toEqual(mockDate);
vi.useRealTimers();
});
});
Test Patterns
How do I use test.each for repeated tests?
How do I use test.each for repeated tests?
Copy
describe("Validation", () => {
test.each([
["john@example.com", true],
["invalid-email", false],
["", false],
["test@", false]
])("email validation: %s -> %s", async (email, expected) => {
const isValid = await UserModel.validateEmail(email);
expect(isValid).toBe(expected);
});
});
describe("UserRole", () => {
test.each([
["admin", true],
["user", false],
["guest", false]
])("admin permission: %s -> %s", async (role, expected) => {
const user = await UserModel.findById(1);
user.role = role;
const isAdmin = await UserModel.isAdmin(user);
expect(isAdmin).toBe(expected);
});
});
How do I group tests?
How do I group tests?
Copy
describe("UserModel", () => {
describe("CRUD Operations", () => {
test("create", async () => { });
test("read", async () => { });
test("update", async () => { });
test("delete", async () => { });
});
describe("Validations", () => {
test("email validation", async () => { });
test("password strength", async () => { });
});
describe("Relations", () => {
test("posts", async () => { });
test("profile", async () => { });
});
});
Best Practices
Guidelines for writing tests
Guidelines for writing tests
DO:
- β Each test should be independently executable
- β
Initialize test data with
beforeAllorbeforeEach - β Use meaningful test names
- β Test only one feature per test
- β Always test error cases
- β Donβt create dependencies between tests
- β Donβt call real external services (use Mocks)
- β Donβt use production DB
- β Avoid hard-coded IDs or dates
- β Donβt ignore test failures