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/