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