Testing our code
Patrik Gallik @ LB* 2015
no, I don't mean user testing
Motivation
- to prevent bugs when adding a new feature and unintentionally breaks an old feature (and don't know about it!) => good sleep
- write better, testable code (with TDD)
- translate business logic with acceptance tests (eg behat in PHP world)
Frameworks
- find a testing framework for your technology
- examples in this presentation are using Mocha + Sinon (Javascript) and Codeception (PHP testing framework that uses PHPunit)
Tests
- Unit tests
- Integration tests
- Functional tests
- Acceptance tests
Unit tests
- small parts of code, isolated
- testing particular unit, faking other modules/methods the unit is using
- mocks (test if method was called)
- stubs (fake what functions returns)
Text
<?php
public function testIfFormattingAmountWithCurrencyWorks()
{
$this->tester->assertEquals(
'10.50 €',
Helpers::format_currency('10.50', 'eur')
);
$this->tester->assertEquals(
'10.50',
Helpers::format_currency('10.50', 'undefined')
);
$this->tester->assertEquals(
'10 000 000 000.00 $',
Helpers::format_currency('10000000000', 'USD')
);
$this->tester->assertEquals(
'156.70 Kč',
Helpers::format_currency('156.7', 'CZK')
);
}
var expect = chai.expect;
describe("MyModule", function() {
var sandbox;
beforeEach(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(window.console, "log");
});
afterEach(function() {
sandbox.restore();
});
describe("test function", function() {
it("should return that it is Batman", function() {
expect(MyModule.testMe()).to.equal("I am batman!");
});
it("should log something to a console", function() {
MyModule.testMe();
sinon.assert.calledOnce(console.log);
sinon.assert.calledWithExactly(console.log, "test log");
})
});
});
Integration tests
- test multiple parts of code (methods, modules) working together
- functional tests
- using test data (database), but not using stubs/mocks
<?php
$I = new FunctionalTester($scenario);
$I->am('a user');
$I->wantTo('create a new recipient in my workspace');
$I->amLoggedAs(['email' => 'patresk@lbstudio.sk', 'password' => 'patresk']);
$I->amOnPage('/app/recipients');
$I->click('.col-lg-6.text-right a.btn-primary');
$I->seeCurrentUrlEquals('/app/recipients/create');
$I->fillField('name', 'Complex recipient');
$I->fillField('account_number_pre', '');
$I->fillField('account_number', '0123456789');
$I->fillField('bank_code', '1234');
$I->click('Uložiť');
$I->seeCurrentUrlEquals('/app/recipients');
$I->seeInDatabase('recipients', [
'name' => 'Complex recipient',
'account_number' => '0123456789',
'bank_code' => '1234'
]);
Acceptance tests
- test our code from client (user) perspective
- end to end (e2e) tests
- the app is black box - we don't test internal implementation, if a function was called etc, we care about what user see
- automated tests with Webdriver (Selenium, PhantomJS)
<?php
class RecipientManagementCest
{
public function addRecipient(AcceptanceTester $I)
{
$I->am('a user');
$I->wantTo('add a new recipient');
$I->amOnPage('/app/recipients');
$I->click('Pridať prijímateľa');
$I->seeInCurrentUrl('/app/recipients/create');
$I->fillField('name', 'Test recipient');
$I->fillField('account_number_pre', '');
$I->fillField('account_number', '0123456789');
$I->fillField('bank_code', '1234');
$I->click('Uložiť');
$I->seeCurrentUrlEquals('/app/recipients');
$I->see('Prijímateľ bol úspešne vytvorený.');
$I->see('Test recipient');
}
}
bugs will always remain, but at least not critical ones
If you find a bug, create a test to reproduce the bug, do refactor and make the test pass -> regression testing!
Learn
https://nicolas.perriault.net/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/
http://www.smashingmagazine.com/2014/10/07/introduction-to-unit-testing-in-angularjs/
Testing our code
By patrikgallik
Testing our code
A small presentation I've made for my fellaz in LB* to explain them the process of testing our code, when developing one of our internal apps.
- 1,160