Getting Started

Introduction

Fauji is a lightweight testing framework for JavaScript applications. Designed with simplicity and developer experience in mind, Fauji provides a Jest-like API with powerful assertion capabilities, comprehensive mocking system, and built-in async testing support.

Installation

npm install fauji --save-dev

Configuration

Fauji requires a simple configuration file fauji.config.json to define test discovery patterns and source directories. You must create a fauji.config.json file in your project root for Fauji to work.

{
  "sourceDirectory": "src",
  "testMatch": [
    "**/*.test.js",
    "**/*.spec.js"
  ]
}
  • sourceDirectory - Directory containing your source code (default: current directory)
  • testMatch - Array of glob patterns to match test files

Allowed Test Patterns

  • **/*.test.js - Files ending with .test.js
  • **/*.spec.js - Files ending with .spec.js
  • **/*.test.ts - Files ending with .test.ts
  • **/*.spec.ts - Files ending with .spec.ts

⚠️ Important: Without this configuration file, npx fauji command will exit with an error: "fauji.config.json not found in the project root"

Without Configuration

When you specify all necessary options via command line, the config file becomes optional:

# These work without config file:
npx fauji --dir ./tests --pattern "*.test.js"
npx fauji --dir ./src --pattern "*.spec.js" --name "auth"
npx fauji --dir ./components --pattern "*.test.js" --watch

Flexible to support different project structures

No matter what type of test file structure you follow, Fauji automatically discovers tests in your project based on the testMatch pattern mentioned in the config file.

Co-located Tests

src/
├── components/
│   ├── Button.js
│   ├── Button.test.js
│   ├── Modal.js
│   └── Modal.test.js
├── utils/
│   ├── math.js
│   ├── math.test.js
│   ├── string.js
│   └── string.test.js
└── services/
    ├── api.js
    ├── api.test.js
    ├── auth.js
    └── auth.test.js

If you follow the co-located test structure like above, you just need to specify the sourceDirectory in the config file as src and you are good to go.

{
  "sourceDirectory": "./src",
  "testMatch": ["**/*.test.js", "**/*.spec.js"]
}

Separate Test Directory

src/
├── components/
│   ├── Button.js
│   └── Modal.js
├── utils/
│   ├── math.js
│   └── string.js
└── services/
    ├── api.js
    └── auth.js

tests/
├── components/
│   ├── Button.test.js
│   └── Modal.test.js
├── utils/
│   ├── math.test.js
│   └── string.test.js
└── services/
    ├── api.test.js
    └── auth.test.js

If you prefer to keep your tests in a separate directory like above, you just need to specify the testDirectory in the config file as tests and you are good to go.

{
  "sourceDirectory": "./tests",
  "testMatch": ["**/*.test.js", "**/*.spec.js"]
}
💡 Pro Tip: CLI options always take precedence over configuration file settings. This allows you to override defaults for specific use cases while maintaining a base configuration.

Writing Your First Test

Create a test file (e.g., math.test.js) and start writing tests using Fauji's intuitive API:


describe('Math Operations', () => {
  test('addition works correctly', () => {
    expect(2 + 2).toBe(4);
    expect(0.1 + 0.2).toBeCloseTo(0.3);
  });

  test('handles edge cases', () => {
    expect(Infinity + 1).toBe(Infinity);
    expect(NaN + 1).toBeNaN();
  });
});

// Calculator module test
function add(a, b) {
  return a + b;
}

function divide(a, b) {
  if (b === 0) throw new Error('Division by zero');
  return a / b;
}

describe('Calculator', () => {
  test('addition', () => {
    expect(add(5, 3)).toBe(8);
    expect(add(-1, 1)).toBe(0);
  });

  test('division', () => {
    expect(divide(10, 2)).toBe(5);
    expect(() => divide(10, 0)).toThrow('Division by zero');
  });
});
            
          

Test Structure and Organization

Fauji follows Jest's familiar test structure with describe blocks for grouping and test functions for individual test cases:

            
describe('User Authentication', () => {
  // Setup that runs before all tests in this describe block
  beforeAll(() => {
    console.log('Setting up authentication tests');
  });

  // Setup that runs before each test
  beforeEach(() => {
    // Reset any state before each test
  });

  describe('Login functionality', () => {
    test('successful login with valid credentials', () => {
      const user = { username: 'john', password: 'secret123' };
      const result = authenticate(user);
      
      expect(result).toMatchObject({
        success: true,
        user: { username: 'john' }
      });
    });

    test('failed login with invalid credentials', () => {
      const user = { username: 'john', password: 'wrong' };
      const result = authenticate(user);
      
      expect(result.success).toBe(false);
      expect(result.error).toContain('Invalid credentials');
    });
  });

  describe('Token validation', () => {
    test('validates JWT tokens correctly', () => {
      const token = 'valid.jwt.token';
      expect(validateToken(token)).toBeTruthy();
    });

    test('rejects invalid tokens', () => {
      expect(validateToken('invalid-token')).toBeFalsy();
      expect(validateToken(null)).toBeFalsy();
      expect(validateToken('')).toBeFalsy();
    });
  });

  // Cleanup after each test
  afterEach(() => {
    // Clean up any test data
  });

  // Cleanup after all tests in this describe block
  afterAll(() => {
    console.log('Authentication tests completed');
  });
});
            
          

Essential Matchers Overview

Fauji provides a comprehensive set of matchers for different testing scenarios:

            
describe('Essential Matchers Demo', () => {
  test('equality matchers', () => {
    // Strict equality (Object.is)
    expect(42).toBe(42);
    expect('hello').toBe('hello');
    
    // Deep equality for objects and arrays
    expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 });
    expect([1, 2, 3]).toEqual([1, 2, 3]);
  });

  test('type checking matchers', () => {
    expect([1, 2, 3]).toBeArray();
    expect({ key: 'value' }).toBeObject();
    expect('hello world').toBeString();
    expect(42).toBeNumber();
    expect(true).toBeBoolean();
    expect(() => {}).toBeFunction();
    expect(new Date()).toBeDate();
    expect(/pattern/).toBeRegExp();
  });

  test('truthiness and nullish matchers', () => {
    expect(true).toBeTruthy();
    expect('non-empty').toBeTruthy();
    expect(1).toBeTruthy();
    
    expect(false).toBeFalsy();
    expect('').toBeFalsy();
    expect(0).toBeFalsy();
    
    expect(null).toBeNull();
    expect(undefined).toBeUndefined();
    expect('defined').toBeDefined();
  });

  test('string and pattern matchers', () => {
    expect('Hello World').toMatch('World');
    expect('user@example.com').toMatch(/\w+@\w+\.\w+/);
    expect('Hello World').toContain('Hello');
  });

  test('array and collection matchers', () => {
    expect([1, 2, 3, 4]).toContain(3);
    expect([1, 2, 3]).toHaveLength(3);
    expect('hello').toHaveLength(5);
    expect(new Set([1, 2, 3])).toHaveLength(3);
  });

  test('object property matchers', () => {
    const user = { 
      name: 'John', 
      age: 30, 
      address: { city: 'New York', zip: '10001' } 
    };
    
    expect(user).toHaveProperty('name');
    expect(user).toHaveProperty('name', 'John');
    expect(user).toHaveProperty('address.city', 'New York');
    expect(user).toMatchObject({ name: 'John', age: 30 });
  });

  test('numerical comparison matchers', () => {
    expect(10).toBeGreaterThan(5);
    expect(10).toBeGreaterThanOrEqual(10);
    expect(5).toBeLessThan(10);
    expect(5).toBeLessThanOrEqual(5);
    expect(0.1 + 0.2).toBeCloseTo(0.3, 1);
  });
});
            
          

Running Tests

Fauji provides a simple and powerful CLI for running your tests. Here are the essential commands to get you started:

Quick Start

# Run all tests in current directory
npx fauji

# Run tests in a specific directory
npx fauji --dir ./tests

# Run tests with a specific pattern
npx fauji --pattern "*.test.js"

Essential Options

# Watch mode - automatically rerun tests when files change
npx fauji --watch

# Run only tests containing "auth" in the filename
npx fauji --name "auth"

# Combine options for targeted testing
npx fauji --dir ./src --name "user" --watch

Common Use Cases

  • Development: npx fauji --watch - Run tests continuously
  • Specific Feature: npx fauji --name "login" - Test only login-related files
  • Directory Testing: npx fauji --dir ./src/components - Test only components
  • CI/CD: npx fauji --parallel - Run tests in parallel for faster execution
💡 Pro Tip: Use npx fauji --help to see all available options, or check the complete CLI reference for advanced features like parallel execution, environment options, and performance optimization.

Next Steps

Now that you have Fauji set up and understand the basics, explore these advanced features: