Jasmine 학습
1. 기본 사용법 (Test Suite and Spec)
- describe함수는 spec을 그룹화 합니다.
- describe 함수는 suite의 제목과 구현하는 코드블록을 인자로 갖는다.
- test spec은 it 함수로 정의한다.
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
2. 평가
- expect는 평가의 대상이 되는 값이다.
- toBe 는 Matcher로 예상값을 예측한다 (toBe 외에 많은 Matcher들이 있다)
- toBe (정확이 같은 객체인지 평가)
it("and has a positive case", function() {
expect(true).toBe(true);
});
it("and can have a negative case", function() {
expect(false).not.toBe(true);
});
it("works for simple literals and variables", function() {
var a = 12;
expect(a).toEqual(12);
});
it("The 'toMatch' matcher is for regular expressions", function() {
var message = "foo bar baz";
expect(message).toMatch(/bar/);
expect(message).toMatch("bar");
expect(message).not.toMatch(/quux/);
});
- toBeDefined or toBeUndefined (값이 정의되어 있는지 평가)
it("The 'toBeDefined' matcher compares against `undefined`", function() {
var a = {
foo: "foo"
};
expect(a.foo).toBeDefined();
expect(a.bar).not.toBeDefined();
});
it("The `toBeUndefined` matcher compares against `undefined`", function() {
var a = {
foo: "foo"
};
expect(a.foo).not.toBeUndefined();
expect(a.bar).toBeUndefined();
});
it("The 'toBeNull' matcher compares against null", function() {
var a = null;
var foo = "foo";
expect(null).toBeNull();
expect(a).toBeNull();
expect(foo).not.toBeNull();
});
- toBeTruthy, toBeFalsy (값이 true인지, flase 인지 평가)
it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
var a, foo = "foo";
expect(foo).toBeTruthy();
expect(a).not.toBeTruthy();
});
- toContain (배열 또는 스트링의 포함여부 평가)
it("works for finding an item in an Array", function() {
var a = ["foo", "bar", "baz"];
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
it("also works for finding a substring", function() {
var a = "foo bar baz";
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
- toBeLessThan, toBeGreaterThan(값이 작은지 큰지 평가)
it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
var pi = 3.1415926, e = 2.78;
expect(e).toBeLessThan(pi);
expect(pi).not.toBeLessThan(e);
});
it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
var pi = 3.1415926,
e = 2.78;
expect(pi).toBeGreaterThan(e);
expect(e).not.toBeGreaterThan(pi);
});
- toBeCloseTo(소수점아래 몇자리까지 같은지 평가)
it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
var pi = 3.1415926,
e = 2.78;
expect(pi).not.toBeCloseTo(e, 2);
expect(pi).toBeCloseTo(e, 0);
});
- toThrow(예외가 제대로 던져 지는지 평가)
it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
var foo = function() {
return 1 + 2;
};
var bar = function() {
return a + 1;
};
var baz = function() {
throw 'what';
};
expect(foo).not.toThrow();
expect(bar).toThrow();
expect(baz).toThrow('what');
});
2. spec을 강제로 실패시킴
describe("A spec using the fail function", function() {
it("should not call the callBack", function() {
fail("Callback has been called");
});
});
2. Setup and Teardown
- beforeEach, afterEach (각 스팩을 실행할때마다, 각 스팩을 실행한 후에)
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() {
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
- beforeAll, afterAll (모든 스팩을 실행하기 전에, 모든 스팩을 실행한 후에ss)
- 동일한 수트안의 함수는 동일은 오브젝트 this를 갖는다
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
3. 비활성화와 팬딩
xdescribe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
});
- Spec 비활성화
- xit을 사용
- function 인자를 넘기지 않음
- pending 함수 사용
describe("Pending specs", function() {
xit("can be declared 'xit'", function() {
expect(true).toBe(false);
});
it("can be declared with 'it' but without a function");
it("can be declared by calling 'pending' in the spec body", function() {
expect(true).toBe(false);
pending('this is why it is pending');
});
});
4. 스파이 (이중테스트)
- toHaveBeenCalled (메서드 호출되었는지 평가)
- toHaveBeenCalledTimes (메서드 호출횟수 평가)
- toHaveBeenCalledWith (메서드 호출중했던 인자중 같은 동일한 호출이 있었는지 평가)
- and.callThrough (스파이로 연결된 메서드는 실제로 호출되지 않음으로 실제 실행시켜준다)
- and.returnValue (스파이로 연결된 메서드가 실행될때마다 특정값을 리턴)
- and.callFake (다른 함수로 바꿔치기)
- and.throw (스파이로 연결된 메서드 호출시 오류를 던짐)
- calls.any (적어도 한번 호출되었는지 평가)
- calls.count (호출된 횟수를 반환)
- calls.argsFor (호출 인덱스에 인자를 반환)
- calls.allArgs (모든 호출에 대한 인수를 반환)
- calls.all (호출의 모든 arguments 와컨텍스트를 반환)
- calls.mostRecent (가장 최근호출의 모든 arguments 와컨텍스트를 반환)
- calls.first (가장 첫번째 호출의 모든 arguments 와컨텍스트를 반환)
- calls.reset (모든 스파이의 기록을 전부 지움)
- jasmine.createSpy (빈껍데기 스파이 함수롤 만든다)
- jasmine.createSpyObj (빈껍데기 스파이 객체를 만든다)
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
it("tracks that the spy was called x times", function() {
expect(foo.setBar).toHaveBeenCalledTimes(2);
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
5. jasmine.any or jasmine.anything
- jsmine.any (생성자 이름을 비교하여 평가)
describe("jasmine.any", function() {
it("matches any value", function() {
expect({}).toEqual(jasmine.any(Object));
expect(12).toEqual(jasmine.any(Number));
});
});
- jasmine.anything (실제값을 정확히 몰를경우 사용하면 true를 반환)
describe("jasmine.anything", function() {
it("matches anything", function() {
expect(1).toEqual(jasmine.anything());
});
describe("when used with a spy", function() {
it("is useful when the argument can be ignored", function() {
var foo = jasmine.createSpy('foo');
foo(12, function() {
return false;
});
expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
});
});
});
6. 특정 부분의 매칭
- jasmine.objectContaining (특정 키 / 값 에만 관심을 갖는 경우의 평가)
describe("jasmine.objectContaining", function() {
var foo;
beforeEach(function() {
foo = {
a: 1,
b: 2,
bar: "baz"
};
});
it("matches objects with the expect key/value pairs", function() {
expect(foo).toEqual(jasmine.objectContaining({
bar: "baz"
}));
expect(foo).not.toEqual(jasmine.objectContaining({
c: 37
}));
});
});
- jasmine.arrayContaining (배열에 포함되어 있는지 매칭)
describe("jasmine.arrayContaining", function() {
var foo;
beforeEach(function() {
foo = [1, 2, 3, 4];
});
it("matches arrays with some of the values", function() {
expect(foo).toEqual(jasmine.arrayContaining([3, 1]));
expect(foo).not.toEqual(jasmine.arrayContaining([6]));
});
});
- jasmine.stringMatching (문자열의 특정부분과 매칭을 평가)
describe('jasmine.stringMatching', function() {
it("matches as a regexp", function() {
expect({foo: 'bar'}).toEqual({foo: jasmine.stringMatching(/^bar$/)});
expect({foo: 'foobarbaz'}).toEqual({foo: jasmine.stringMatching('bar')});
});
});
7. Custom Matcher
describe("custom asymmetry", function() {
var tester = {
asymmetricMatch: function(actual) {
var secondValue = actual.split(',')[1];
return secondValue === 'bar';
}
};
it("dives in deep", function() {
expect("foo,bar,baz,quux").toEqual(tester);
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback('foo,bar,baz');
expect(callback).toHaveBeenCalledWith(tester);
});
});
});
8. 시간에 종속적인 테스트
- jasmine.clock().install (시간을 조작해야 하는 스팩에 사용)
- jasmine.clock().uninstall (시간을 조작하는 테스트가 끝난 후 초기화)
- jasmine.clock().tick (밀리세컨드 단위로 시간을 조작)
- jasmine.clock().mockDate (현재 시간을 변경한 목 데이터 사용)
describe("Manually ticking the Jasmine Clock", function() {
var timerCallback;
beforeEach(function() {
timerCallback = jasmine.createSpy("timerCallback");
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("causes a timeout to be called synchronously", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.clock().tick(101);
expect(timerCallback).toHaveBeenCalled();
});
});
9. 비동기 테스팅
- done() (beforEach, it, afterEach는 비동기 수행이 완료되었을때 호출되는 아규먼트를 가지고 있다)
describe("Using callbacks", function() {
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
});
- soon().then (promise 를 사용하여 비동기 처리를 할수있다.)
describe("Using promises", function() {
if (!browserHasPromises()) {
return;
}
beforeEach(function() {
return soon().then(function() {
value = 0;
});
});
it("should support async execution of test preparation and expectations", function() {
return soon().then(function() {
value++;
expect(value).toBeGreaterThan(0);
});
});
});
- await soon() (async await을 사용하여 비동기 처리를 할 수 있다.)
describe("Using async/await", function() {
if (!browserHasAsyncAwaitSupport()) {
return;
}
beforeEach(async function() {
await soon();
value = 0;
});
it("should support async execution of test preparation and expectations", async function() {
await soon();
value++;
expect(value).toBeGreaterThan(0);
});
});
10. jasmin-ajax 플러그인
- jasmine.Ajax.install() (사용할 ajax test의 spec의 시작전에 호출)
- jasmine.Ajax.uninstall() (사용할 ajax test의 spec의 종류 후에 호출)
- jasmine.Ajax.stubRequest() (호출 즉시 반환해야할때)
- jasmine.Ajax.requests.mostRecent().respondWith (success호출을 기다려야할때)
describe("suite wide usage", function() {
beforeEach(function() {
jasmine.Ajax.install();
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
it("specifying response when you need it", function() {
var doneFn = jasmine.createSpy("success");
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(args) {
if (this.readyState == this.DONE) {
doneFn(this.responseText);
}
};
xhr.open("GET", "/some/cool/url");
xhr.send();
expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
expect(doneFn).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"contentType": 'text/plain',
"responseText": 'awesome response'
});
expect(doneFn).toHaveBeenCalledWith('awesome response');
});
it("allows responses to be setup ahead of time", function () {
var doneFn = jasmine.createSpy("success");
jasmine.Ajax.stubRequest('/another/url').andReturn({
"responseText": 'immediate response'
});
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(args) {
if (this.readyState == this.DONE) {
doneFn(this.responseText);
}
};
xhr.open("GET", "/another/url");
xhr.send();
expect(doneFn).toHaveBeenCalledWith('immediate response');
});
});