Presentation is loading. Please wait.

Presentation is loading. Please wait.

PHPUnit vs PHPSpec © Rudolf Horváth

Similar presentations


Presentation on theme: "PHPUnit vs PHPSpec © Rudolf Horváth"— Presentation transcript:

1 PHPUnit vs PHPSpec © Rudolf Horváth
Unit Testing PHPUnit vs PHPSpec © Rudolf Horváth

2 Unit Testing The primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as you expect. Each unit is tested separately before integrating them into modules to test the interfaces between modules.

3 A good test Able to be fully automated
Has full control over all the pieces running (Use mocks or stubs to achieve this isolation when needed) Can be run in any order if part of many other tests Runs in memory (no DB or File access, for example) Consistently returns the same result (Typically no random numbers. Save those for integration or range tests) Runs fast Tests a single logical concept in the system Readable Maintainable Trustworthy (when you see its result, you don’t need to debug the code just to be sure)

4 PHPUnit The tests for a class Class go into a class ClassTest.
ClassTest inherits (most of the time) from PHPUnit_Framework_TestCase. The tests are public methods that are named test*. Alternatively, you can use annotation in a method's docblock to mark it as a test method. Inside the test methods, assertion methods such as assertEquals() are used to assert that an actual value matches an expected value.

5 Simple example

6 Test dependencies To quickly localize defects, we want our attention to be focussed on relevant failing tests. This is why PHPUnit skips the execution of a test when a depended-upon test has failed. This improves defect localization by exploiting the dependencies between tests. There can be multiple dependencies

7 Data Providers Must return Iterator, or arry

8 Testing Exceptions Or simply try {} catch and see if it’s there

9 Assertions assertArrayHasKey(mixed $key, array $array[, string $message = '']) assertContains(mixed $needle, Iterator|array $haystack[, string $message = '']) assertContainsOnly(string $type, Iterator|array $haystack[, boolean $isNativeType = NULL, string $message = '']) assertContainsOnlyInstancesOf(string $classname, Traversable|array $haystack[, string $message = '']) assertCount($expectedCount, $haystack[, string $message = '']) assertEmpty(mixed $actual[, string $message = '']) assertEquals(mixed $expected, mixed $actual[, string $message = '']) assertFalse(bool $condition[, string $message = '']) assertTrue(bool $condition[, string $message = '']) assertFileExists(string $filename[, string $message = '']) assertGreaterThan(mixed $expected, mixed $actual[, string $message = '']) assertGreaterThanOrEqual(mixed $expected, mixed $actual[, string $message = '']) assertInstanceOf($expected, $actual[, $message = '']) assertNull(mixed $variable[, string $message = '']) assertSame(mixed $expected, mixed $actual[, string $message = '']) Every assertion has a negative “Not” version with the same parameters

10 AssertThat

11 Fixtures

12 Fixtures setUp(), tearDown()once for each test method (and on fresh instances) of the test case class. setUpBeforeClass(), tearDownAfterClass()before the first test of the test case class is run and after the last test of the test case class is run, respectively. assertPreConditions() assertPostConditions() onNotSuccessfulTest() you only need to implement tearDown() if you have allocated external resources like files or sockets in setUp(). If your setUp() just creates plain PHP objects, you can generally ignore tearDown()

13 Let’s try those… Download previous lesson’s zip, launch mysql server
composer require --dev phpunit/phpunit ^5.7 (6.1 is only for PHP 7+) Change files DataLoader.php (disable output), carService (allow null Request parameter) Change DefaultControllerTest to CarControllerTest Start CarServiceTest phpunit --tap

14 Test doubles Sometimes it is just plain hard to test the system under test (SUT) because it depends on other components that cannot be used in the test environment. This could be because they aren't available, they will not return the results needed for the test or because executing them would have undesirable side effects. In other cases, our test strategy requires us to have more control or visibility of the internal behavior of the SUT. 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!

15 Test doubles getMock($className)
By default, all methods of the original class are replaced with a dummy implementation that just returns NULL (without calling the original method). Using the will($this->returnValue()) method, for instance, you can configure these dummy implementations to return a value when called. final, private and static methods cannot be stubbed or mocked. They retain their original behavior.

16 Stubs

17 Stubs (with MockBuilder)

18 Stubs (with MockBuilder)
setConstructorArgs(array $args) setMockClassName($name) disableOriginalConstructor() disableOriginalClone() disableAutoload() any() once() never() atLeastOnce() exactly() at(index) returnValue() returnArgument() returnSelf() returnCallback() (throwException())

19 Code Coverage Code Coverage

20 Test Driven Development
Drives out the functionality the software actually needs, rather than what the programmer thinks it probably ought to have When to write test? - Long after development - Shortly after development or - before? Problems (?): - When you first write the test it cannot possibly run - Feels strange at first (programming to an interface instead of programming to an implementation)

21 Behaviour-Driven Development
Traditional TDD tools still force developers to think in terms of tests and assertions instead of specifications. It's about figuring out what you are trying to do before you run off half-cocked to try to do it. You write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and executable form. It's all about specifying behaviour and not writing tests. Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting. And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable.

22 The other way

23 PHPSpec Test class names end with “Spec”.
The spec extends the special ObjectBehavior class. This class is special, because it gives you ability to call all the methods of the class you are describing and match the result of the operations against your expectations. Method names are underscored, starting with “it_” or “its_”

24 Matchers Matchers are much like assertions, except the fact that matchers concentrate on telling how the object should behave instead of verifying how it works. Identity (return, be, equal, beEqualTo) it’s like checking === Comparison (beLike) it’s like checking == Throw (throw -> during) for testing exceptions Type (beAnInstanceOf, returnAnInstanceOf, haveType) checks object type ObjectState (have**) checks object is** method return value

25 Matchers

26 Identity matcher

27 Comparison matcher

28 Throw matcher

29 Type matcher

30 ObjectState matcher

31 Count matcher

32 Scalar matcher

33 Stubs - Prophecy $prophecy = $prophet->prophesize('MyClass');
$prophecy->read('123')->willReturn('value'); ReturnPromise [->willReturn(1)] returns a value from a method call ReturnArgumentPromise [->willReturnArgument()] returns the first method argument from call ThrowPromise [->willThrow] causes the method to throw specific exception CallbackPromise [->will($callback)] gives you a quick way to define your own custom logic Keep in mind, that you can always add even more promises by implementing Prophecy\Promise\PromiseInterface.

34 Stubs Method prophecy Argument wildcarding

35 Stubs - Argument wildcards
ExactValueToken [Argument::exact($value)] checks that the argument matches a specific value TypeToken [Argument::type($typeOrClass)] checks that the argument matches a specific type or classname. ObjectStateToken [Argument::which($method, $value)] checks that the argument method returns a specific value CallbackToken [Argument::that(callback)] checks that the argument matches a custom callback AnyValueToken [Argument::any()] matches any argument AnyValuesToken [Argument::cetera()] matches any arguments to the rest of the signature

36 Mocks and call predictions
$prophet->someMethod()->shouldBeCalled() CallPrediction [shouldBeCalled()] checks that the method has been called 1 or more times NoCallsPrediction [shouldNotBeCalled()] checks that the method has not been called CallTimesPrediction [shouldBeCalledTimes($count)] checks that the method has been called $count times CallbackPrediction [should($callback)] checks the method against your own custom callback Verify predictions with $prophet->checkPredictions();

37 Spies Prophecy records every call made during the double's entire lifetime. This means you don't need to record predictions in order to check them. You can also do it manually MethodProphecy::shouldHave(PredictionInterface $prediction)

38 let and letGo class MyMailingServiceSpec extends ObjectBehavior {
public function let($mailer) $mailer->beAMockOf('\Swift_Mailer'); $this->beConstructedWith($mailer); } public function letgo() // release any resource // put the system back into the state it was


Download ppt "PHPUnit vs PHPSpec © Rudolf Horváth"

Similar presentations


Ads by Google