JavaScript ist eine sehr dynamische Sprache und verhält sich zudem je nach Browser unterschiedlich. Daher sind automatisierte Tests besonders wertvoll. Dieser Vortrag zeigt, wie Cross-Browser-Tests für JavaScript entwickelt werden können.
Vortrag von der MobileTech Conference 2011
13. Hello
Jasmine!
describe("parseFloat",
function()
{
it("should
return
undefined
for
undefined",
function
()
{
expect(util.parseFloat(undefined)).toBeUndefined();
});
it("should
return
0
for
empty
string",
function
()
{
expect(util.parseFloat('')).toEqual(0);
});
it("should
return
number
for
number
strings",
function
()
{
expect(util.parseFloat('1.5')).toEqual(1.5);
});
//
...
});
14. Hello
Jasmine!
describe("parseFloat",
function()
{
Suite
it("should
return
undefined
for
undefined",
function
()
{
expect(util.parseFloat(undefined)).toBeUndefined();
});
it("should
return
0
for
empty
string",
function
()
{
expect(util.parseFloat('')).toEqual(0);
});
it("should
return
number
for
number
strings",
function
()
{
expect(util.parseFloat('1.5')).toEqual(1.5);
});
//
...
});
15. Hello
Jasmine!
describe("parseFloat",
function()
{
it("should
return
undefined
for
undefined",
function
()
{
expect(util.parseFloat(undefined)).toBeUndefined();
});
Sp cs
it("should
return
0
for
empty
string",
function
()
{
e
expect(util.parseFloat('')).toEqual(0);
});
it("should
return
number
for
number
strings",
function
()
{
expect(util.parseFloat('1.5')).toEqual(1.5);
});
//
...
});
16. Hello
Jasmine!
describe("parseFloat",
function()
{
it("should
return
undefined
for
undefined",
function
()
{
expect(util.parseFloat(undefined)).toBeUndefined();
});
Matc
it("should
return
0
for
empty
string",
function
()
{
expect(util.parseFloat('')).toEqual(0);
her
});
it("should
return
number
for
number
strings",
function
()
{
expect(util.parseFloat('1.5')).toEqual(1.5);
});
//
...
});
21. Spies
describe("execute
fadein",
function()
{
it("should
eventually
set
opacity
to
1",
function
()
{
var
element
=
document.createElement("div");
spyOn(window,
'setTimeout').andCallFake(
function(callback)
{
callback();
});
spyOn(util,
'opacity');
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
22. Spies
describe("execute
fadein",
function()
{
it("should
eventually
set
opacity
to
1",
function
()
{
var
element
=
document.createElement("div");
spyOn(window,
'setTimeout').andCallFake(
function(callback)
{
callback();
});
spyOn(util,
'opacity');
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
23. Spies
describe("execute
fadein",
function()
{
Arran
ge
it("should
eventually
set
opacity
to
1",
function
()
{
var
element
=
document.createElement("div");
spyOn(window,
'setTimeout').andCallFake(
function(callback)
{
callback();
});
spyOn(util,
'opacity');
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
24. Spies
describe("execute
fadein",
function()
{
Arran
it("should
eventually
set
opacity
to
1",
function
()
{
ge
var
element
=
document.createElement("div");
spyOn(window,
'setTimeout').andCallFake(
function(callback)
{
callback();
});
spyOn(util,
'opacity');
//
waits
for
'delay'
seconds
fadein.execute(element);
//
then
calls
callback
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
window.setTimeout
=
function
(callback,
delay)
{
});
...
};
});
25. Spies
describe("execute
fadein",
function()
{
Arran
it("should
eventually
set
opacity
to
1",
function
()
{
ge
var
element
=
document.createElement("div");
//
set
opacity
of
'element'
to
'opacity'
util.opacity
=
function
(element,
opacity)
{
spyOn(window,
'setTimeout').andCallFake(
...
function(callback)
{
};
callback();
});
spyOn(util,
'opacity');
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
26. Spies
describe("execute
fadein",
function()
{
it("should
eventually
set
opacity
to
1",
function
()
{
var
element
=
document.createElement("div");
spyOn(window,
'setTimeout').andCallFake(
function(callback)
{
callback();
});
Act
spyOn(util,
'opacity');
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
27. Spies
describe("execute
fadein",
function()
{
it("should
eventually
set
opacity
to
1",
function
()
{
var
element
=
document.createElement("div");
//
set
opacity
of
'element'
to
'opacity'
util.opacity
=
function
(element,
opacity)
{
spyOn(window,
'setTimeout').andCallFake(
...
function(callback)
{
};
callback();
});
Asser
spyOn(util,
'opacity');
t
fadein.execute(element);
expect(util.opacity.mostRecentCall.args[1]).toEqual(1);
});
});
28. FunkLon
Der
Spy
soll...
spy.andCallThrough()
...
die
ursprüngliche
FunkLon
aufrufen
spy.andReturn(argument)
...
das
übergebene
Argumente
zurückgeben
spy.andThrow(exception)
...
die
übergebene
ExcepLon
werfen
spy.andCallFake(function)
...
die
übergebene
FunkLon
aufrufen
FunkLonen
von
Spies
29. FunkLon
Prüb,
ob
der
Spy...
toHaveBeenCalled()
...
aufrufen
wurde
toHaveBeenCalledWith(args)
...
mit
den
übergebenen
Argumenten
aufgerufen
wurde
Matcher
für
Spies
FunkLon
Bedeutung
spy.callCount
Anzahl
der
Aufrufe
spy.argsForCall[i]
Argumente
des
i-‐ten
Aufrufs
spy.mostRecentCall.args
Argumente
des
letzten
Aufrufs
Eigenschaben
von
Spies
31. Asynchrone
Tests
it("should
eventually
set
opacity
to
1",
function()
{
var
element
=
document.createElement("div");
spyOn(util,
'opacity');
runs(function()
{
fadein.execute(element);
});
waitsFor(function
()
{
return
opacitySpy.mostRecentCall.args[1]
===
1;
});
runs(function()
{
//
some
other
expectations
expect(opacitySpy.mostRecentCall.args[1]).toEqual(1);
});
});
32. Asynchrone
Tests
it("should
eventually
set
opacity
to
1",
function()
{
var
element
=
document.createElement("div");
spyOn(util,
'opacity');
Arran
runs(function()
{
ge
fadein.execute(element);
});
waitsFor(function
()
{
return
opacitySpy.mostRecentCall.args[1]
===
1;
});
runs(function()
{
//
some
other
expectations
expect(opacitySpy.mostRecentCall.args[1]).toEqual(1);
});
});
33. Asynchrone
Tests
it("should
eventually
set
opacity
to
1",
function()
{
var
element
=
document.createElement("div");
spyOn(util,
'opacity');
runs(function()
{
Act
fadein.execute(element);
});
waitsFor(function
()
{
return
opacitySpy.mostRecentCall.args[1]
===
1;
});
runs(function()
{
//
some
other
expectations
expect(opacitySpy.mostRecentCall.args[1]).toEqual(1);
});
});
34. Asynchrone
Tests
it("should
eventually
set
opacity
to
1",
function()
{
var
element
=
document.createElement("div");
spyOn(util,
'opacity');
runs(function()
{
fadein.execute(element);
Wait
});
...
waitsFor(function
()
{
return
opacitySpy.mostRecentCall.args[1]
===
1;
});
runs(function()
{
//
some
other
expectations
//
set
opacity
of
'element'
to
'opacity'
expect(opacitySpy.mostRecentCall.args[1]).toEqual(1);
util.opacity
=
function
(element,
opacity)
{
});
...
});
};
35. Asynchrone
Tests
it("should
eventually
set
opacity
to
1",
function()
{
var
element
=
document.createElement("div");
spyOn(util,
'opacity');
runs(function()
{
fadein.execute(element);
});
waitsFor(function
()
{
return
opacitySpy.mostRecentCall.args[1]
===
1;
});
Asser
runs(function()
{
t
//
some
other
expectations
expect(opacitySpy.mostRecentCall.args[1]).toEqual(1);
});
});
38. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
runs(function()
{
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
39. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
.
loadHtml("/js-‐fadein/index.html");
age..
dex
p
runs(function()
{
n
The
i
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
40. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
Arran
runs(function()
{
ge,
P
art
1
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
41. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
Arran
runs(function()
{
ge,
P
art
2
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
42. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
runs(function()
{
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
Act
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
43. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
runs(function()
{
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
Wait
...
waitsForAsync();
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
44. it("should
fade
in
hello
div
on
button
click",
function
()
{
var
win,
field,
button;
loadHtml("/js-‐fadein/index.html");
runs(function()
{
win
=
testwindow();
field
=
win.document.getElementById('hello');
button
=
win.document.getElementById('fadein');
});
runs(function
()
{
expect(win.util.opacity(field)).toEqual(0);
jasmine.ui.simulate(button,
'click');
});
Asser
waitsForAsync();
t
runs(function
()
{
expect(win.util.opacity(field)).toEqual(1);
});
});
UI-‐Test
mit
Jasmine-‐UI
46. Pro
Contra
Beispiele
-‐
Schwerer
zu
Im
Laufen
in
QUnit
automaLsieren
Browser
ProdukLons-‐
YUI
Test
testen
-‐
Schwerer
in
CI
umgebung
Jasmine
einzubeaen
-‐
Leicht
zu
Envjs
Headless
automaLsieren
Browser
wird
PhantomJS
Browser
-‐
Leicht
in
CI
nur
simuliert
Zombie.js
einzubeaen
HtmlUnit
50. Fazit
Es
gibt
ein
umfangreiches
Toolset
zum
Testen
von
JavaScript-‐Code.
Testen
Sie
Ihren
Code!
Testen
Sie
insbesondere
Ihren
JavaScript-‐Code!
Sie
haben
keine
Ausrede...
52. saturated
wriLng
(By
Eduordo,
hap://www.flickr.com/photos/tnarik/366393127/)
Smiley
Keyboard
(By
~Prescoa,
hap://www.flickr.com/photos/ppym1/93571524/)
Spyhole,
Paris
(By
smith
of
smiths,
hap://www.flickr.com/photos/hesketh/232015302/)
Sorta
synchronised
diving
(By
Aaron
Retz,
hap://www.flickr.com/photos/aretz/309469339/)
53. Stamp
Collector
(By
C.
CasLllo,
hap://www.flickr.com/photos/carolinedecasLllo/2750577684/)
bios
[bible]
(By
Gastev,
hap://www.flickr.com/photos/gastev/2174505811/)
Day
276/365:
in
hot
pursuit
(By
Tina
Vega,
hap://www.flickr.com/photos/Lnavega/3066602429/)
54. Besuchen
Sie
uns
Morgen
beim
Vortrag
JavaScript
Data
Binding
mit
jQuery
Mobile
um
10:00
Uhr
im
Watordsaal