What we talk when we talk about testing

Zvonimir Spajic

 

software developer

You Are Testing

  •  manual
  •  automated
  •  production ?
  •  QA teams

Manual Testing

  • slow
  • inefficient
  • error prune

Automated Testing

  • fast(er)
  • write once run forever
  • serve as specification
  • confidence in your code -> not afraid to refactor

 

"Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behaviour of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse." - Michael C. Feather, Working Effectively With Legacy Code

 

Testing Layers

UI Layer

  • testing "from the outside"
  • slow
  • does not require good code
  • sensitive to ui changes
<?php
$I->amOnPage('/login');
$I->fillField('username', 'Ivica');
$I->fillField('password', 'Todoric');
$I->click('LOGIN');
$I->see('Welcome mister Todoric!');
<?php   
    /**
     * @When I add a job named :jobName
     * @param $jobName
     */
    public function iAddAJobNamed($jobName)
    {
        $this->getSession()->visit('jobs');
        $this->getSession()->clickLink('Add Job');
        $this->getSession()->fillField('Name',$jobName);
        $this->getSession()->clickLink('Save');
    }
  Scenario:
    When I add a job named "Direktor Agrokora"
    Then I should see "Direktor Agrokora" in jobs list

Service Layer

  • testing by making service calls
  • faster than ui (no web server)
  • service acrhitecture
<?php

  public function testAddingToBasket()
    {
        $basket = new Basket(new Storage());
        $basket->add('Test Product');
        $this->assertContains('Test Product', $basket->getProducts());
    }
  Scenario:
    When I add a job named "Direktor Agrokora"
    Then I should see "Direktor Agrokora" in jobs list
<?php   
    /**
     * @When I add a job named :jobName
     * @param $jobName
     */
    public function iAddAJobNamed($jobName)
    {
        $this->jobList->addJob($jobName);
    }

    /**
     * @Then I should see a :jobName
     * @param $jobName
     */
    public function iShouldSeeA($jobName)
    {
        assert(in_array($jobName,$this->jobList->getJobs());
    }

Unit Layer

  • testing individual classes
  • requires good code and architecture
  • very fast
<?php   
public function testAddition()
{
    $calculator = new Calculator();
    $calculator->add(3,3);
    $this->assertEquals(6,$calculator->getResult());
}

public function testMultiplication()
{
    $calculator = new Calculator();
    $calculator->multiply(3,3);
    $this->assertEquals(9,$calculator->getResult());
}
<?php 
class FooService
{
    // ...

    public function getMessage()
    {
        $data = $this->callsProvider->getCalls();
        return $this->generateMessage($data);
    }

    private function generateMessage($data)
    {
        // do some data processing and generate message
    }
}
<?php 
public function testForNoCalls()
{
    $callProvider = $this->createMock(CallsProvider::class);
    $callProvider->method('getCalls')
        ->willReturn('{"data":[]}');

    $fooService = new FooService($callProvider);
    
    $message = $fooService->getMessage();
    $this->assertEquals('Sorry, no messages for you', $message);
}

Test Double  - a "fake" object that has the same public API as the object it is "faking"

 

<?php 

public function it_returns_propper_for_multiple_calls(CallsProvider $callsProvider) 
{
    $callsProvider->getCalls()
        ->willReturn('{"data":["Davor","Zdravko","Kolinda"]}');
    
    $this->getMessage()->shouldBe('You Have 3 messages');
}

When we are writing a test in which we cannot (or chose not to) use a real depended-on component (DOC), we can replace it with a Test Double. The Test Double doesn't have to behave exactly like the real DOC; it merely has to provide the same API as the real one so that the SUT thinks it is the real one!

  • Fake
  • Stub
  • Mock
  • Dumy Object
  • Spy

Testing pyramid

Test Types

  • Acceptance
  • Functional
  • Integration
  • Unit
  • Regression
  • ...

Test Driven Development

  •  You can't write any production code until you have first written a failing unit test.

 

  •  You can't write more of a unit test than is sufficient to fail, and not compiling is failing.

 

  • You can't write more production code than is sufficient to pass the currently failing unit test.

Testing Evolution:

  • manual testing
  • automated testing
  • test-first development
  • test-driven development

QA

?

Resources

Made with Slides.com