Jasmine 方法

Jasmine 測試方法詳解與示範

Jasmine 是一個用於 JavaScript 單元測試的框架,它提供了豐富的方法來進行各種類型的斷言和測試組織。以下是 Jasmine 的主要方法分類與詳細說明:

測試結構

describe

用於分組相關的測試案例:

describe('Calculator', () => {
  // 測試案例
});

it

定義單一測試案例:

it('should add two numbers correctly', () => {
  expect(calculator.add(1, 2)).toBe(3);
});

beforeEach

在每個測試案例執行前運行:

describe('Database test', () => {
  beforeEach(() => {
    // 設置測試資料
    db.clear();
    db.insert({id: 1, name: 'Test'});
  });
  
  it('should find records', () => {
    // 測試...
  });
});

afterEach

在每個測試案例執行後運行:

afterEach(() => {
  // 清理資源
  db.close();
});

beforeAll

在所有測試案例執行前只運行一次:

beforeAll(() => {
  // 建立昂貴的資源,例如資料庫連接
  db = new Database();
});

afterAll

在所有測試案例執行後只運行一次:

afterAll(() => {
  // 關閉連接
  db.disconnect();
});

fdescribe / fit

只運行特定的測試組或測試案例:

fdescribe('重點測試', () => {
  // 只有這個組內的測試會執行
});

fit('重點測試案例', () => {
  // 只有這個測試案例會執行
});

xdescribe / xit

暫時禁用特定的測試組或測試案例:

xdescribe('暫時忽略的測試', () => {
  // 這個組內的測試不會執行
});

xit('暫時忽略的測試案例', () => {
  // 這個測試案例不會執行
});

基本匹配器

toBe

嚴格相等 (使用 ===):

expect(actual).toBe(expected);
expect(true).toBe(true);
expect(1).toBe(1);

toEqual

深度相等 (適用於物件比較):

expect({a: 1, b: 2}).toEqual({a: 1, b: 2});
expect([1, 2, 3]).toEqual([1, 2, 3]);

toMatch

使用正則表達式匹配字串:

expect('hello world').toMatch(/world/);
expect('test@example.com').toMatch(/^[a-z]+@[a-z]+\.[a-z]+$/);

toContain

檢查陣列或字串是否包含特定元素:

expect([1, 2, 3]).toContain(2);
expect('hello world').toContain('world');
expect(new Set([1, 2, 3])).toContain(2);

toBeDefined

檢查變數是否已定義:

expect(variable).toBeDefined();

toBeUndefined

檢查變數是否未定義:

expect(variable).toBeUndefined();

toBeNull

檢查變數是否為 null:

expect(variable).toBeNull();

toBeTruthy

檢查值是否為真值:

expect(1).toBeTruthy();
expect('hello').toBeTruthy();
expect(true).toBeTruthy();

toBeFalsy

檢查值是否為假值:

expect(0).toBeFalsy();
expect('').toBeFalsy();
expect(false).toBeFalsy();
expect(null).toBeFalsy();

toBeGreaterThan

檢查數值是否大於特定值:

expect(10).toBeGreaterThan(5);

toBeGreaterThanOrEqual

檢查數值是否大於等於特定值:

expect(10).toBeGreaterThanOrEqual(10);

toBeLessThan

檢查數值是否小於特定值:

expect(5).toBeLessThan(10);

toBeLessThanOrEqual

檢查數值是否小於等於特定值:

expect(5).toBeLessThanOrEqual(5);

toBeCloseTo

檢查浮點數是否約等於特定值 (避免浮點數精度問題):

expect(0.1 + 0.2).toBeCloseTo(0.3, 5); // 5位小數精度

toBeNaN

檢查值是否為 NaN:

expect(NaN).toBeNaN();
expect(0/0).toBeNaN();

toBePositiveInfinity

檢查值是否為正無窮:

expect(1/0).toBePositiveInfinity();

toBeNegativeInfinity

檢查值是否為負無窮:

expect(-1/0).toBeNegativeInfinity();

物件匹配器

toHaveSize

檢查集合大小 (適用於 Array, Set, Map 等):

expect([1, 2, 3]).toHaveSize(3);
expect(new Set([1, 2])).toHaveSize(2);
expect(new Map([['a', 1], ['b', 2]])).toHaveSize(2);

toContain

檢查集合是否包含特定元素:

expect([1, 2, 3]).toContain(2);
expect(new Set([1, 2, 3])).toContain(2);

toHaveProperty

檢查物件是否擁有特定屬性:

expect({name: 'John', age: 30}).toHaveProperty('name');
expect({name: 'John', age: 30}).toHaveProperty('name', 'John');
expect({user: {name: 'John'}}).toHaveProperty('user.name', 'John');

DOM 相關匹配器 (Angular 測試常用)

toHaveClass

檢查元素是否有特定 CSS 類:

// HTML: <div class="active highlight">Element</div>
expect(element).toHaveClass('active');
expect(element).toHaveClass('active highlight');

toContainText

檢查元素文本是否包含特定字串:

// HTML: <div>Hello World</div>
expect(element).toContainText('Hello');

toHaveText

檢查元素的完整文本:

// HTML: <div>Hello World</div>
expect(element).toHaveText('Hello World');

toHaveValue

檢查表單元素的值:

// HTML: <input value="test">
expect(inputElement).toHaveValue('test');

toBeChecked

檢查複選框是否被選中:

// HTML: <input type="checkbox" checked>
expect(checkboxElement).toBeChecked();

toBeDisabled

檢查元素是否被禁用:

// HTML: <button disabled>Click</button>
expect(buttonElement).toBeDisabled();

toBeVisible

檢查元素是否可見:

expect(element).toBeVisible();

toBeInTheDocument

檢查元素是否在文檔中:

expect(element).toBeInTheDocument();

非同步測試

done 回調

處理非同步測試的傳統方式:

it('should handle async operations', (done) => {
  someAsyncFunction().then(result => {
    expect(result).toBe('expected value');
    done();
  });
});

async/await

更現代的非同步測試方式:

it('should handle async operations', async () => {
  const result = await someAsyncFunction();
  expect(result).toBe('expected value');
});

waitForAsync (Angular 特有)

用於 Angular 的非同步測試:

import { waitForAsync } from '@angular/core/testing';

it('should handle async operations', waitForAsync(() => {
  component.ngOnInit();
  fixture.whenStable().then(() => {
    fixture.detectChanges();
    expect(component.data).toBe('expected value');
  });
}));

fakeAsync (Angular 特有)

用於 Angular 的非同步測試,可使用 tick() 函數:

import { fakeAsync, tick } from '@angular/core/testing';

it('should handle timeout', fakeAsync(() => {
  let called = false;
  setTimeout(() => { called = true; }, 100);
  expect(called).toBe(false);
  tick(50);
  expect(called).toBe(false);
  tick(50);
  expect(called).toBe(true);
}));

Spy 功能

spyOn

監視對象的方法調用:

const calculator = { add: (a, b) => a + b };
spyOn(calculator, 'add');
calculator.add(1, 2);
expect(calculator.add).toHaveBeenCalled();
expect(calculator.add).toHaveBeenCalledWith(1, 2);

spyOn 並回傳值

監視方法並返回指定值:

spyOn(calculator, 'add').and.returnValue(10);
expect(calculator.add(1, 2)).toBe(10);

createSpy

創建獨立的 spy 函數:

const spy = jasmine.createSpy('mySpy');
spy(1, 2);
expect(spy).toHaveBeenCalledWith(1, 2);

createSpyObj

創建帶有多個 spy 方法的對象:

const mockService = jasmine.createSpyObj('service', ['getData', 'setData']);
mockService.getData.and.returnValue('test data');
expect(mockService.getData()).toBe('test data');

toHaveBeenCalled

檢查 spy 是否被調用:

expect(spy).toHaveBeenCalled();

toHaveBeenCalledTimes

檢查 spy 被調用的次數:

expect(spy).toHaveBeenCalledTimes(2);

toHaveBeenCalledWith

檢查 spy 被調用時的參數:

expect(spy).toHaveBeenCalledWith('arg1', 'arg2');

其他實用功能

pending

標記測試為待處理:

it('is pending test', () => {
  pending('這個測試還沒實現');
  // 以下代碼將不會運行
});

fail

手動使測試失敗:

it('should fail on purpose', () => {
  if (someCondition) {
    fail('測試失敗的原因');
  }
});

not

反轉匹配器結果:

expect(value).not.toBe(10);
expect(array).not.toContain('banned');

Angular 特有功能

ComponentFixture

操作 Angular 元件:

const fixture = TestBed.createComponent(MyComponent);
const component = fixture.componentInstance;
fixture.detectChanges(); // 觸發變更檢測

任何時候你要檢查 DOM 的狀態(無論是存在還是不存在),都需要先呼叫 detectChanges()

規則總結

  • 只檢查元件實例屬性值(例如 @Input 預設值):不需要 detectChanges()

  • 檢查 DOM 渲染或元件與視圖交互:需要 detectChanges()

DebugElement

查詢 DOM 元素:

const button = fixture.debugElement.query(By.css('button'));
button.nativeElement.click(); // 模擬點擊

dispatchEvent

發送 DOM 事件:

const input = fixture.debugElement.query(By.css('input')).nativeElement;
input.value = 'new value';
input.dispatchEvent(new Event('input'));
fixture.detectChanges();

Last updated