2. WHO AM I?
• Sebastian Springer
• aus München
• arbeite bei Mayflower
• https://github.com/sspringer82
• @basti_springer
• Consultant,Trainer,Autor
3.
4. Was erwartet euch heute?
BDD Tests mit CucumberJS
End2End Tests mit CasperJS
Unittesting mit Jasmine
ein paar Tools
und hoffentlich viele Antworten auf eure Fragen
6. cucumber.js
Behaviour-Driven Development (BDD) für JavaScript.
Basiert auf Node.js
Port von Cucumber auf JavaScript.
Tests werden in der DSL Gherkin geschrieben und auf der
Kommandozeile ausgeführt.
Getestet mit Node, Firefox, Chrome, Opera, Safari.
7. Ziel
Formulierung von Tests in menschlicher Sprache. Eigentlich
sollten auch Nicht-Entwickler Tests schreiben können.
9. Aufbau
Ein Feature besteht aus einem oder mehreren Szenarien.
Diese Szenarien setzen sich aus verschiedenen Steps
zusammen.
Aufbau eines Szenarios:
- Given
- When
- Then
Optionale Erweiterung:
- And
- But
10. Aufbau
Die Dateien werden standardmäßig im aktuellen Verzeichnis
in einem Unterverzeichnis features erwartet.
Die Dateien enden auf .feature.
11. addition.feature
Feature: Calculator can add two numbers
As a user of a calculator
I want to add two numbers
So I get the result and don't have to calculate it by myself
Scenario: Add 1 and 1
Given I have a calculator instance
When I add 1 and 1
Then the result is 2
Scenario: Add 1 and "Hello"
Given I have a calculator instance
When I add 1 and "Hello"
Then an exception will be thrown
13. $ cucumber-js
Feature: Calculator can add two numbers
As a user of a calculator
I want to add two numbers
So I get the result and don't have to calculate it by myself
Scenario: Add 1 and 1 # features/addition.feature:6
Given I have a calculator instance # features/addition.feature:7
When I add 1 and 1 # features/addition.feature:8
Then the result is 2 # features/addition.feature:9
Scenario: Add 1 and "Hello" # features/addition.feature:11
Given I have a calculator instance # features/addition.feature:12
When I add 1 and "Hello" # features/addition.feature:13
Then an exception will be thrown # features/addition.feature:14
2 scenarios (2 undefined)
6 steps (6 undefined)
14. You can implement step definitions for undefined steps with these
snippets:
this.Given(/^I have a calculator instance$/, function (callback) {
// Write code here that turns the phrase above into concrete
actions
callback.pending();
});
16. Step-Definitionen
Hier verlassen wir die schöne Welt der verständlichen Sprache
und wenden uns wieder der Programmierung zu.
Die Step-Definitionen liegen im features-Verzeichnis in einem
eigenen Ordner mit dem Namen step_definitions.
Die Step-Definitionen werden als Node.js-Module erstellt. Es
kann auf den kompletten Funktionsumfang von Node.js
zurückgegriffen werden.
17. Step-Definitionen
module.exports = function () {
var result;
this.Given(/^I have a calculator instance$/, function (callback) {
calc = new Calculator();
callback();
});
};
18. Ausführung
$ cucumber-js
Feature: Calculator can add two numbers
As a user of a calculator
I want to add two numbers
So I get the result and don't have to calculate it by myself
Scenario: Add 1 and 1 # features/addition.feature:6
Given I have a calculator instance # features/addition.feature:7
When I add 1 and 1 # features/addition.feature:8
Then the result is 2 # features/addition.feature:9
Scenario: Add 1 and "Hello" # features/addition.feature:11
Given I have a calculator instance # features/addition.feature:12
When I add 1 and Hello # features/addition.feature:13
Then an exception will be thrown # features/addition.feature:14
2 scenarios (2 passed)
6 steps (6 passed)
20. Fehlschlag
$ cucumber-js
Feature: Calculator can add two numbers
As a user of a calculator
I want to add two numbers
So I get the result and don't have to calculate it by myself
Scenario: Add 1 and 1 # features/addition.feature:6
Given I have a calculator instance # features/addition.feature:7
When I add 1 and 1 # features/addition.feature:8
Then the result is 2 # features/addition.feature:9
AssertionError: result should be 4
at World.<anonymous> (/Use…
Failing scenarios:
/Use…ator/features/addition.feature:6 # Scenario: Add 1 and 1
2 scenarios (1 failed, 1 passed)
6 steps (1 failed, 5 passed)
21. Hooks
Werden verwendet um die Umgebung vorzubereiten oder
aufzuräumen.
Empfehlung: unter features/support/hooks.js ablegen.
module.exports = function () {
this.Before(function (callback) {
// before each test
callback();
});
};
module.exports = function () {
this.After(function (callback) {
// after each scenario
callback();
});
};
36. Test
casper.test.begin('add 1 and 1', 1, function suite(test) {
var calc = new Calculator();
test.assertEquals(calc.add(1, 1), 2);
test.done();
});
37. Test
$ casperjs test test2.js
Test file: test2.js
# add 1 and 1
PASS Subject equals the expected value
PASS 1 test executed in 0.024s, 1 passed, 0 failed, 0 dubious, 0
skipped.
38. Test
casper.test.begin('Submit Google Form', function suite(test) {
casper.start('http://www.google.de', function () {
test.assertExists('input[name="q"]');
this.fill('form', {
q: 'Hello Kitty'
}, true)
});
casper.then(function () {
test.assertTextExists('Hello Kitty – Wikipedia', 'there is a
wiki entry');
});
casper.run(function () {
test.done();
})
});
39. Test
$ casperjs test test3.js
Test file: test3.js
# Submit Google Form
PASS Find an element matching: input[name="q"]
PASS there is a wiki entry
PASS 2 tests executed in 1.852s, 2 passed, 0 failed, 0 dubious, 0
skipped.
40. CasperJS
CasperJS hat eine sehr umfangreiche API. Allerdings auch
eine gute Dokumentation mit vielen Beispielen dazu.
http://docs.casperjs.org/
44. Protractor/Selenium
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('https://angularjs.org');
element(by.model('todoList.todoText')).sendKeys('write 1 protractor test');
element(by.css('[value="add"]')).click();
var todoList = element.all(by.repeater('todo in todoList.todos'));
expect(todoList.count()).toEqual(3);
expect(todoList.get(2).getText()).toEqual('write first protractor test');
// You wrote your first test, cross it off the list
todoList.get(2).element(by.css('input')).click();
var completedAmount = element.all(by.css('.done-true'));
expect(completedAmount.count()).toEqual(2);
});
});
46. Jasmine
BDD-Framework zur Erstellung von Unittests für
Applikationen.
Sehr weit verbreitet und gute Community-Unterstützung.
Unabhängig von Frameworks.
Alternativen: Mocha, QUnit, …
48. Ein erster Test
describe('Calculator', function () {
it('should add 1 and 1 and return 2', function () {
var calc = new Calculator();
var result = calc.add(1, 1);
expect(result).toBe(2);
});
});
49. Matcher
Matcher Bedeutung
toBe Typsichere Prüfung auf Gleichheit
toEqual Erweiterte Prüfung auf Gelichheit anhand einer
internenFunktion
toMatch Prüfung gegen einen regulären Ausdruck
toBeDefined Prüft, ob ein Ausdruck definiert ist
toBeTruthy Prüft, ob ein Ausdruck wahr ist, nicht typsicher
toBeFalsy Prüft, ob ein Ausdruck falsch ist, nicht
typsicher
50. Hooks
describe('Calculator', function () {
var calc;
beforeEach(function () {
calc = new Calculator();
});
afterEach(function () {
calc = null;
});
it('should add 1 and 1 and return 2', function () {
var result = calc.add(1, 1);
expect(result).toBe(2);
});
});
52. Karma
Das Standalone-Release von Jasmine ist für den täglichen
Gebrauch nicht geeignet. Ein Entwickler muss ständig
zwischen IDE und Browser hin und her wechseln.
Besser: Infrastruktur und Einbettung in die
Entwicklungsumgebung
55. Initialisierung
$ karma init
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine
Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no
Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
>
Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>
Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes
Config file generated at "/Users/sspringer/srv/conf/2015/enterjs/karma/karma.conf.js".
60. Spy
Wrapper um eine Funktion. Aufrufe werden direkt an die
ursprüngliche Funktion weitergeleitet. Aufrufe werden
aufgezeichnet.
Spy-Objekt kann später abgefragt werden.
Spys sollten später zurückgesetzt werden.
61. Spy
it("should create a spy for a method", function () {
var myObj = {
name: 'Klaus',
getName: function () {
return this.name;
}
};
var spy = sinon.spy(myObj, 'getName');
myObj.getName();
expect(spy.calledOnce).toBeTruthy();
myObj.getName.restore();
});
63. Stub
Wrapper um eine Funktion wie Spys. Weisen ein definiertes
Verhalten auf. Leiten den Aufruf nicht direkt an die
ursprüngliche Funktion weiter.
Reduzieren Abhängigkeiten und vereinfachen
Testumgebungen.
64. Stub
it('should return and throw', function () {
var myObj = {
getName: function () {},
setName: function () {}
}
var stub1 = sinon.stub(myObj, 'getName').returns('Klaus')
var stub2 = sinon.stub(myObj, ‘setName').throws(
new Error(‘BOOH!’)
);
expect(stub1()).toBe('Klaus');
});
66. Mock
Ebenfalls Wrapper um eine Funktion. Dienen dazu, die
korrekte Verwendung einer Funktion sicherzustellen. Wird
die Funktion nicht korrekt verwendet, wird eine Exception
geworfen.
Best Practice: Nicht mehr als ein Mock pro Test.
67. Mock
it ('should work with mocks', function () {
var myObj = {
name: 'Klaus',
getName: function () {
return this.name;
}
}
var mock = sinon.mock(myObj);
mock.expects('getName').once();
myObj.getName();
myObj.getName();
mock.verify();
});
72. Server
Wrapper um XMLHttpRequest bzw. ActiveXObject. Tests
werden unabhängig von einer Server-Infrastruktur ausgeführt.
Kontrolle über die Antworten des Fake Servers.
75. Fixtures
HTML-Struktur, die für die Tests benötigt wird.
Unabhängigkeit der Tests soll verbessert werden. Durch
vorbereitete Strukturen können Ausschnitte von Workflows
getestet werden.
Auslieferung von HTML über den html2js preprocessor von
Karma.
jasmine-jquery als Helper für den Umgang mit Fixtures.