Tests¶
Controller test case¶
Ouzo provides ControllerTestCase
which allows you to verify that:
- there’s a route for a given url
- controllers methods work as expected
- views are rendered without errors
<?php
class UsersControllerTest extends ControllerTestCase
{
/**
* @test
*/
public function shouldRenderIndex()
{
//when
$this->get('/users');
//then
$this->assertRenders('Users/index');
}
/**
* @test
*/
public function shouldRedirectToIndexOnSuccessInCreate()
{
//when
$this->post('/users', [
'user' => [
'login' => 'login'
]]
);
//then
$this->assertRedirectsTo(usersPath());
}
}
Methods¶
get($url)
- mock GET request for given urlpost($url, $data)
- mock POST request with data for given urlput($url, $data)
- mock PUT request with data for given urlpatch($url)
- mock PATCH request for given urldelete($url)
- mock DELETE request for given urlgetAssigned($name)
- get value of $name variable assigned to the rendered view.getRenderedJsonAsArray()
- get returned JSON as arraygetResponseHeaders()
- get all response header
Assertions¶
assertRedirectsTo($path)
assertRenders($viewName)
- asserts that the given view was renderedassertAssignsModel($variable, $modelObject)
- asserts that a model object was assigned to a viewassertDownloadsFile($file)
assertAssignsValue($variable, $value)
assertRenderedContent()
- returns StringAssert for rendered content.assertRenderedJsonAttributeEquals($attribute, $equals)
assertResponseHeader($expected)
Database test case¶
Ouzo provides DbTransactionalTestCase
class that takes care of transactions in tests.
This class starts a new transaction before each test case and rolls it back afterwards.
<?php
class UserTest extends DbTransactionalTestCase
{
/**
* @test
*/
public function shouldPersistUser()
{
//given
$user = new User(['name' => 'bob']);
//when
$user->insert();
//then
$storedUser = User::where(['name' => 'bob'])->fetch();
$this->assertEquals('bob', $storedUser->name);
}
}
Model assertions¶
Assert::thatModel
allows you to check if two model objects are equal.
Sample usage¶
<?php
class UserTest extends DbTransactionalTestCase
{
/**
* @test
*/
public function shouldPersistUser()
{
//given
$user = new User(['name' => 'bob']);
//when
$user->insert();
//then
$storedUser = User::where(['name' => 'bob'])->fetch();
Assert::thatModel($storedUser)->isEqualTo($user);
}
}
Assertions¶
isEqualTo($expected)
- compares all attributes. If one model has loaded a relation and other has not, they are considered not equal. Attributes not listed in model’s fields are also comparedhasSameAttributesAs($expected)
- compares only attributes listed in Models fields
String assertions¶
Assert::thatString
allows you to check strings as a fluent assertions.
Sample usage¶
Assert::thatString("Frodo")
->startsWith("Fro")->endsWith("do")
->contains("rod")->doesNotContain("fro")
->hasSize(5);
Assert::thatString("Frodo")->matches('/Fro\w+/');
Assert::thatString("Frodo")->isEqualToIgnoringCase("frodo");
Assert::thatString("Frodo")->isEqualTo("Frodo");
Assert::thatString("Frodo")->isEqualNotTo("asd");
Assertions¶
contains($substring)
- check that string contains substringdoesNotContain($substring)
- check that string does not contains substringstartsWith($prefix)
- check that string is start with prefixendsWith($postfix)
- check that string is end with postfixisEqualTo($string)
- check that string is equal to expectedisEqualToIgnoringCase($string)
- check that string is equal to expected (case insensitive)isNotEqualTo($string)
- check that string not equal to expectedmatches($regex)
- check that string is fit to regexphasSize($length)
- check string lengthisNull()
- check a string is nullisNotNull()
- check a string is not nullisEmpty()
- check a string is emptyisNotEmpty()
- check a string is not empty
Array assertions¶
Assert::thatArray
is a fluent array assertion to simplify your tests.
Sample usage¶
<?php
$animals = ['cat', 'dog', 'pig'];
Assert::thatArray($animals)->hasSize(3)->contains('cat');
Assert::thatArray($animals)->containsOnly('pig', 'dog', 'cat');
Assert::thatArray($animals)->containsExactly('cat', 'dog', 'pig');
Note
Array assertions can also be used to examine array of objects. Methods to do this is onProperty
and onMethod
.
Using onProperty
:
<?php
$object1 = new stdClass();
$object1->prop = 1;
$object2 = new stdClass();
$object2->prop = 2;
$array = [$object1, $object2];
Assert::thatArray($array)->onProperty('prop')->contains(1, 2);
Using onMethod
:
Assert::thatArray($users)->onMethod('getAge')->contains(35, 24);
Assertions¶
contains($element ..)
- vararg elements to examine that array contains themcontainsOnly($element ..)
- vararg elements to examine that array contains only themcontainsExactly($element ..)
- vararg elements to examine that array contain exactly elements in pass orderhasSize($expectedSize)
- check size of the arrayisNotNull()
- checks if array is not nullisEmpty()
- checks if array is emptyisNotEmpty()
- checks if array is not emptycontainsKeyAndValue($elements)
containsSequence($element ..)
- checks if vararg sequence exists in the arrayexcludes($element ..)
hasEqualKeysRecursively(array $array)
isEqualTo($array)
keys()
- next assertions will be against array keys, not values
Exception assertions¶
CatchException enables you to write a unit test that checks that an exception is thrown.
Sample usage¶
//given
$foo = new Foo();
//when
CatchException::when($foo)->method();
//then
CatchException::assertThat()->isInstanceOf("FooException");
Session assertions¶
Assert::thatSession
class comes with a handy method to test your session content.
Sample usage¶
// when
Session::set('key1', 'value1');
->set('key2', 'value2');
// then
Assert::thatSession()
->hasSize('2')
->contains('key2' => 'value2');
Note
This assert has the same method as Assert::thatArray
.
Testing time-dependent code¶
We do recommend you to use Clock instead of DateTime. Clock provides time travel and time freezing capabilities, making it simple to test time-dependent code.
//given
Clock::freeze('2011-01-02 12:34');
//when
$result = Clock::nowAsString('Y-m-d');
//then
$this->assertEquals('2011-01-02', $result);
See also
Mocking¶
Ouzo provides a Mockito like mocking library that allows you to write tests in BDD (given when then) or AAA (arrange act assert) fashion.
You can stub method calls:
$mock = Mock::create();
Mock::when($mock)->method(1)->thenReturn('result');
$result = $mock->method(1);
$this->assertEquals("result", $result);
And then verify interactions:
//given
$mock = Mock::create();
//when
$mock->method("arg");
//then
Mock::verify($mock)->method("arg");
Unlike other PHP mocking libraries you can verify interactions ex post facto which is more natural and fits BDD or AAA style.
Note
Mock::mock() is an alias for Mock::create(). You can use those methods interchangeably.
If you use type hinting and the mock has to be of a type of a Class, you can pass the required type to Mock::create
method.
$mock = Mock::create('Foo');
$this->assertTrue($mock instanceof Foo);
You can stub a method to throw an exception;
Mock::when($mock)->method()->thenThrow(new Exception());
Verification that a method was not called:
Mock::verify($mock)->neverReceived()->method("arg");
Making sure that there were no interactions:
Mock::verifyZeroInteractions($mock);
You can stub multiple calls in one call to thenReturn:
$mock = Mock::create();
Mock::when($mock)->method(1)->thenReturn('result1', 'result2');
Mock::when($mock)->method()->thenThrow(new Exception('1'), new Exception('2'));
Both thenReturn and thenThrow accept multiples arguments that will be returned/thrown in subsequent calls to a stubbed method.
$mock = Mock::create();
Mock::when($mock)->method()->thenReturn('result1', 'result2');
$this->assertEquals("result1", $mock->method());
$this->assertEquals("result2", $mock->method());
You can stub a method to return value calculated by a callback function:
Mock::when($mock)->method(Mock::any())->thenAnswer(function (MethodCall $methodCall) {
return $methodCall->name . ' ' . Arrays::first($methodCall->arguments);
});
Argument matchers¶
- Mock::any() - matches any value for an argument at the given position
Mock::verify($mock)->method(1, Mock::any(), "foo");
- Mock::anyArgList() - matches any possible arguments. It means that all calls to a given method will be matched.
Mock::verify($mock)->method(Mock::anyArgList());
- Mock::argThat() - returns an instance of FluentArgumentMatcher that can chain methods from Functions.
Mock::verify($mock)->method(Mock::argThat()->extractField('name')->equals('Bob'));
Mock::verify($mock)->method('first arg', Mock::argThat()->isInstanceOf('Foo'));
In rare cases, you may need to write your own argument matcher:
class MyArgumentMatcher implements Ouzo\Tests\Mock\ArgumentMatcher {
public function matches($argument) {
return $argument->name == 'Bob' || $argument->surname == 'Smith';
}
}
Mock::verify($mock)->method(new MyArgumentMatcher());
Stream stubbing¶
In some cases you may need to to stub stream wrappers e.g. php://input
. Ouzo provides a special class for this StreamStub
.
- First, you have to register your wrapper:
StreamStub::register('you_wrapper_name');
- Then write something to the wrapper:
StreamStub::$body = 'some content';
- When you’re finished using stream you must unregistered it:
StreamStub::unregister();
Comprehensive example:
StreamStub::register('some_name');
StreamStub::$body = '{"name":"jonh","id":123,"ip":"127.0.0.1"}';
$object = YourClass::readJsonFromWrapper('some_name://input');
$this->assertEquals('john', $object->name);
StreamStub::unregister();