jasmine 是一套 unit testing framework,用起來直覺,也提供一些功能方便我們寫測試,而 karma 是一套 command line test runner, 用來啟動瀏覽器,執行測試程式碼。 angular cli 建立專案時,預設提供這兩樣測試工具,來幫助我們寫單元測試。

建立 spy object

在這邊我有一個 VoterService ,且有一個 deleteVoter 的方法,可以執行刪除的動作,而為了能發 Http 到 Server,Angular 都是用 HTTP 這類別來發送 http request,所以我建構式注入了 HTTP,而我在單元測試的時候,也就可以注入假的 HTTP 物件。

delete voter
  • ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Injectable()
export class VoterService {

constructor(private http: Http) {}

deleteVoter(session: ISession, voterName: string) {
session.voters = session.voters.filter(voter => voter !== voterName);

this.http.delete(`${environment.host}/api/sessions/${session.id}`).catch(this.handleError).subscribe();
}

private handleError(error: Response) {
return Observable.throw(error.statusText);
}
}

使用 createSpyObj 建立假的 HTTP 物件。

參數說明:

  • mockHttp : base name, 可隨意輸入
  • [‘delete’] : Spy 物件有 delete 這個方法
    Spy
    • ts
    1
    let mockHttp = jasmine.createSpyObj('mockHttp', ['delete']);

撰寫完整的單元測試,而這裡我們要確切的指定 delete 方法回傳 Observable 物件,否則 .catch.subscribe 則會錯誤。

  • mockHttp.delete.and.returnValue(Observable.of(false)); : Spy 物件的 delete 方法回傳 Observable , 因為要測試的 deleteVoter 方法,並沒有在 subscribe 裡面執行任何程式碼,所以使用 Observable.of(false) 建立一個 Observable 物件。
    unit test
    • ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    describe('VoterService', () => {
    let voterService: VoterService,
    mockHttp;

    beforeEach(() => {
    mockHttp = jasmine.createSpyObj('mockHttp', ['delete'])
    voterService = new VoterService(mockHttp);
    });

    describe('deleteVoter', () => {

    it('should remove the session from the list of sessions', () => {
    var session = { id: 6, voters: ["joe", "john"]};
    mockHttp.delete.and.returnValue(Observable.of(false));

    voterService.deleteVoter2(<ISession>session, "joe");

    expect(session.voters.length).toBe(1);
    expect(session.voters[0]).toBe("john");
    })

    })


    })

驗證傳入參數

新增另一個 addVoter 的方法,執行 http.post 發送資料到 http://localhost:3000/api/sessions/voters/joe API 位址

addVoter
  • ts
1
2
3
4
5
6
7
8
9
addVoter(session: ISession, voterName: string) {
session.voters.push(voterName);

let headers = new Headers({ 'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});

let url = `${environment.host}/api/sessions/voters/${voterName}`;
this.http.post(url, JSON.stringify({}), options).catch(this.handleError).subscribe();
}

使用 toHaveBeenCalledWith 驗證 post 傳入的網址是否如預期,以及使用 jasmine.any 驗證傳入的物件型別是否如預期。

unit test
  • ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
describe('VoterService', () => {
let voterService: VoterService,
mockHttp;

beforeEach(() => {
mockHttp = jasmine.createSpyObj('mockHttp', ['delete', 'post'])
voterService = new VoterService(mockHttp);
});

describe('addVoter', () => {

it('should call http.post with the right URL', () => {
var session = { id: 6, voters: ["john"]};
mockHttp.post.and.returnValue(Observable.of(false));

voterService.addVoter(<ISession>session, "joe");

expect(mockHttp.post).toHaveBeenCalledWith('http://localhost:3000/api/sessions/voters/joe', "{}", jasmine.any(RequestOptions));
})

})

})

參考

[jasmine sample]
[karma]
[angular-fundamentals]