Jasmine#
Jasmine은 JavaScript 애플리케이션을 위한 행위 주도 개발(BDD) 스타일의 테스팅 프레임워크이다.
간단하고 읽기 쉬운 문법을 제공하여, 테스트 코드가 마치 일반 문장을 읽는 것처럼 자연스럽게 느껴지도록 설계되었다.
특히 assertion이 내장되어 있어 별도의 라이브러리가 필요하지 않다는 장점이 있다.
동기 및 비동기 코드 모두에 대한 자동화된 테스트 실행이 가능하다.
주요 특징#
- 브라우저와 Node.js 환경에서 모두 실행 가능
- 외부 의존성 없이 독립적으로 사용 가능
- DOM이 필요 없음
- 간결하고 이해하기 쉬운 문법 제공
- 풍부하고 직관적인 API 제공
- Python, Ruby 등 다른 언어에서도 사용 가능
- 가독성이 높은 테스트 코드 작성 가능
- 다양한 JavaScript 환경과 도구와의 호환성
- 활발한 커뮤니티 지원
- 설정이 간단하고 리소스 사용이 적음
기본 구조와 문법#
Jasmine의 기본적인 테스트 구조:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| describe('Calculator', function() {
// 테스트 스위트를 설정합니다
let calculator;
beforeEach(function() {
// 각 테스트 케이스 전에 실행됩니다
calculator = new Calculator();
});
it('should add two numbers correctly', function() {
// 개별 테스트 케이스입니다
expect(calculator.add(2, 3)).toBe(5);
});
describe('advanced operations', function() {
// 중첩된 테스트 스위트도 가능합니다
it('should handle negative numbers', function() {
expect(calculator.add(-1, 1)).toBe(0);
});
});
});
|
핵심 개념#
- 스위트(Suite): ‘describe’ 함수로 정의되는 관련 테스트 그룹
- 스펙(Spec): ‘it’ 함수로 정의되는 개별 테스트 케이스
- 기대(Expectation): ’expect’ 함수를 사용한 실제 테스트 assertion
- 매처(Matcher): 기대값과 실제값을 비교하는 함수
테스트 구조화 함수들#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| describe('테스트 스위트 이름', function() {
// beforeAll: 모든 테스트 전에 한 번 실행
beforeAll(function() {
console.log('테스트 스위트 시작');
});
// beforeEach: 각 테스트 케이스 전에 실행
beforeEach(function() {
this.testValue = 42;
});
// afterEach: 각 테스트 케이스 후에 실행
afterEach(function() {
delete this.testValue;
});
// afterAll: 모든 테스트 후에 한 번 실행
afterAll(function() {
console.log('테스트 스위트 종료');
});
});
|
매처(Matcher)#
매처(Matcher): 기대값과 실제값을 비교하는 함수
Jasmine은 다양한 내장 매처를 제공한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| describe('Matchers 예시', function() {
it('demonstrates different matchers', function() {
// 기본적인 비교
expect(true).toBe(true);
expect([1, 2, 3]).toContain(2);
// 타입 체크
expect(null).toBeNull();
expect(undefined).toBeUndefined();
// 숫자 비교
expect(5).toBeGreaterThan(3);
expect(5).toBeLessThan(10);
// 객체/배열 비교
expect({name: 'test'}).toEqual({name: 'test'});
// 정규식 매칭
expect('hello world').toMatch(/world/);
});
});
|
비동기 테스트#
Jasmine은 비동기 코드 테스트를 위한 기능도 제공한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| describe('비동기 테스트', function() {
// done 콜백을 사용한 방식
it('handles async operations', function(done) {
asyncFunction().then(result => {
expect(result).toBe('success');
done();
});
});
// async/await 사용
it('works with async/await', async function() {
const result = await asyncFunction();
expect(result).toBe('success');
});
});
|
스파이(Spies)#
함수 호출을 모니터링하고 모의 구현을 제공한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| describe('Spy 예시', function() {
it('tracks function calls', function() {
const calculator = {
add: function(a, b) { return a + b; }
};
// 함수를 스파이로 만듭니다
spyOn(calculator, 'add');
calculator.add(2, 3);
// 호출 여부와 인자를 검증합니다
expect(calculator.add).toHaveBeenCalled();
expect(calculator.add).toHaveBeenCalledWith(2, 3);
});
});
|
사용자 정의 매처#
필요한 경우 커스텀 매처를 만들 수 있다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| beforeEach(function() {
jasmine.addMatchers({
toBeEvenNumber: function() {
return {
compare: function(actual) {
return {
pass: actual % 2 === 0,
message: `Expected ${actual} to be an even number`
};
}
};
}
});
});
it('uses custom matcher', function() {
expect(4).toBeEvenNumber();
});
|
사용 방법#
- 독립 실행형 버전 다운로드 또는 npm을 통한 설치
- SpecRunner.html 파일을 통한 테스트 실행
- Node.js 환경에서 CLI를 통한 테스트 실행
실제 프로젝트에서의 활용 예시#
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
26
27
28
29
30
31
32
33
| describe('UserService', function() {
let userService;
let mockHttp;
beforeEach(function() {
// 의존성 설정
mockHttp = jasmine.createSpyObj('HttpClient', ['get', 'post']);
userService = new UserService(mockHttp);
});
describe('getUser', function() {
it('fetches user data correctly', async function() {
const mockUser = { id: 1, name: 'Test User' };
mockHttp.get.and.returnValue(Promise.resolve(mockUser));
const user = await userService.getUser(1);
expect(mockHttp.get).toHaveBeenCalledWith('/api/users/1');
expect(user).toEqual(mockUser);
});
it('handles errors appropriately', async function() {
mockHttp.get.and.returnValue(Promise.reject('Network error'));
try {
await userService.getUser(1);
fail('Should have thrown an error');
} catch (error) {
expect(error).toBe('Network error');
}
});
});
});
|
참고 및 출처#
Jasmine Documentation