TubeSum ← Transcribe a video

DPC2020: My Top 10 PHPUnit Tips & Tricks - Juliette Reinders Folmer

0h 38m video Transcribed Jun 15, 2026 D Dutch PHP Conference
Intermediate 12 min read For: PHP developers with basic knowledge of unit testing who want to improve their PHPUnit skills.
738
Views
37
Likes
2
Comments
0
Dislikes
5.3%
📊 Average

AI Summary

Juliette Reinders Folmer presents her top 10 PHPUnit tips and tricks at the Dutch PHP Conference 2020. She covers essential practices for writing effective unit tests, from setting up the test environment to advanced techniques like mutation testing. The talk aims to help developers improve their testing skills and write more reliable code.

[02:14]
Have Tests

Start with the basics: add PHPUnit to composer.json, configure autoloading for the test directory, and create a phpunit.xml.dist file. Use the --generate-configuration option to get started quickly. Begin with a simple, standalone function to write your first test.

[06:09]
Use the Right Assertion

PHPUnit has over 100 assertions. Use assertSame instead of assertEquals for strict type comparisons. Using the correct assertion makes tests more descriptive and simpler. Always have the PHPUnit manual open to find the right assertion.

[08:34]
Don't Just Test the Happy Path

Test unexpected inputs and edge cases. For example, test with quotes within the text or boolean false input. Document expected behavior through tests. Avoid strict types in tests to allow testing of unexpected inputs.

[10:44]
Limit Assertions per Test

Multiple assertions in one test can make it hard to identify which one failed. Use the optional message parameter in assertions to describe each assertion. Alternatively, use data providers to run each test case separately.

[12:21]
Use Data Providers

Data providers allow you to run the same test with multiple inputs and expected outputs. Each test case runs independently, so failures don't stop other cases. Return an array of arrays where each sub-array contains the parameters for one test case.

[14:07]
Don't Use Your Own Code to Create Test Data

Using your own code to generate test data can perpetuate bugs. Instead, use an external data source or your own brain. For complex calculations, use an external tool to verify the expected output.

[16:10]
Name Your Test Cases

Use named keys in data providers to describe each test case. This makes it easier to identify which test failed, especially with many cases. Named test cases also improve readability and maintainability.

[17:58]
Explore Filtering

PHPUnit's filter mechanism allows you to run specific tests by namespace, class, method name, or data provider key. Use --filter with a regex pattern. This is useful for local development when you don't want to run the entire test suite.

[19:33]
Don't Trust Code Coverage, But Examine It

Code coverage can be misleading; 100% coverage doesn't guarantee thorough testing. Use coverage to identify untested code, but don't make it a hard requirement. Configure coverage with a whitelist and logging, and use scripts to run coverage locally.

[26:15]
Skip Tests Conditionally

Use markTestSkipped() or annotations like @requires PHP 7.3 to skip tests conditionally. This is useful for tests that depend on specific PHP versions or extensions. Verbose mode shows skipped tests and reasons.

[30:39]
Cross-Version Compatibility

To support multiple PHPUnit versions, use annotations like @beforeClass instead of relying on method names with return types. This avoids issues with void return types introduced in PHPUnit 8.

[32:19]
Test Your Tests

Use mutation testing with Infection to verify that your tests catch errors. Also, consider using PHPUnit Compatibility and PHPUnit QA rule sets for PHP CodeSniffer to detect deprecated or suboptimal assertions.

[34:37]
Lose Count

You can never have enough tests. Extra tests for edge cases can catch issues from dependency changes or PHP version upgrades. Having a comprehensive test suite helps prevent production bugs.

Juliette emphasizes that writing good tests is an ongoing process. Start small, use the right tools, and continuously improve your test suite to catch bugs early and ensure code reliability.

Clickbait Check

95% Legit

"The title accurately describes the talk's content: a list of 10 actionable PHPUnit tips, all covered in detail."

Mentioned in this Video

Tutorial Checklist

1 02:14 Add PHPUnit to composer.json and configure autoloading for the test directory.
2 03:24 Create a phpunit.xml.dist file with a test suite pointing to the tests directory and a bootstrap file.
3 04:36 Run `phpunit --generate-configuration` to generate the configuration file automatically.
4 04:56 Write your first test: create a test class extending PHPUnit\Framework\TestCase, add a test method, call the function, and assert the result.
5 06:09 Use assertSame() instead of assertEquals() for strict type comparisons.
6 08:34 Add tests for unexpected inputs and edge cases, not just the happy path.
7 10:44 Limit assertions per test or use the message parameter to identify failing assertions.
8 12:21 Use data providers to run multiple test cases with different inputs and expected outputs.
9 14:07 Generate test data from external sources, not from your own code.
10 16:10 Name your test cases in data providers using associative keys.
11 17:58 Use the --filter option to run specific tests by namespace, class, method, or data provider key.
12 19:33 Configure code coverage with a whitelist and logging, and add scripts to run coverage locally.
13 26:15 Use markTestSkipped() or @requires annotations to skip tests conditionally.
14 30:39 Use @beforeClass and @afterClass annotations instead of setUpBeforeClass() and tearDownAfterClass() for cross-version compatibility.
15 32:19 Use mutation testing with Infection to verify test effectiveness.

Study Flashcards (10)

What is the difference between assertEquals and assertSame in PHPUnit?

easy Click to reveal answer

assertEquals uses loose comparison (==), while assertSame uses strict comparison (===).

06:09

Why should you avoid using your own code to generate test data?

medium Click to reveal answer

Using your own code can perpetuate existing bugs; always use an external data source or your own brain.

14:07

What is the purpose of the @covers annotation in PHPUnit?

medium Click to reveal answer

It specifies which method or class a test is intended to cover, ensuring intentional code coverage.

22:43

How can you make a test cross-version compatible with PHPUnit 6, 8, and 9?

hard Click to reveal answer

Use @beforeClass and @afterClass annotations instead of setUpBeforeClass() and tearDownAfterClass() to avoid void return type issues.

30:39

What is mutation testing and which tool is mentioned for it?

medium Click to reveal answer

Mutation testing mutates source code to check if tests fail; the tool mentioned is Infection.

32:53

What does the --filter option in PHPUnit do?

easy Click to reveal answer

It allows you to run specific tests by filtering on namespace, class, method name, or data provider key using a regex pattern.

17:58

How can you mark a test as skipped with a reason in PHPUnit?

medium Click to reveal answer

Use markTestSkipped('reason') or the @requires annotation to skip conditionally, and run PHPUnit in verbose mode to see skipped tests.

27:11

What is the advantage of using data providers in PHPUnit?

medium Click to reveal answer

Data providers allow running the same test with multiple inputs independently; failures don't stop other cases, and each case is reported separately.

12:21

Why should you use named keys in data providers?

easy Click to reveal answer

Named keys make it easier to identify which test case failed, especially with many cases, and improve readability.

16:10

What is the recommended file name for PHPUnit configuration and why?

easy Click to reveal answer

Use phpunit.xml.dist so that developers can override it with phpunit.xml for local environment tweaks.

03:49

💡 Key Takeaways

🔧

Use assertSame over assertEquals

Highlights a common mistake and promotes strict type checking in tests.

06:09
💡

Don't use your own code for test data

Illustrates a real-world pitfall where a developer used the function under test to generate expected results, perpetuating bugs.

14:07
⚖️

Code coverage is not a guarantee

Explains that 100% coverage doesn't mean thorough testing, especially with complex code like regex.

19:33
🔧

Cross-version PHPUnit compatibility

Provides a practical solution for supporting multiple PHPUnit versions using annotations.

30:39
🔧

Test your tests with mutation testing

Introduces Infection as a tool to verify test effectiveness, a concept often overlooked.

32:19

✂️ Creator Tools: Viral Hooks

AI-generated clip ideas for Shorts based on the transcript

Stop Using assertEquals! Use assertSame

60s

High controversy as many developers misuse assertEquals, and the tip is immediately actionable.

▶ Play Clip

Don't Use Your Own Code for Test Data

60s

Relatable anecdote about a developer copying buggy code into tests, making it a cautionary tale that resonates.

▶ Play Clip

Name Your Test Cases for Easy Debugging

60s

Simple yet powerful trick that saves hours of debugging, appealing to developers tired of cryptic test failures.

▶ Play Clip

How to Make Tests Cross-Version Compatible

60s

Solves a common pain point for developers maintaining legacy PHP projects with different PHPUnit versions.

▶ Play Clip

Mutation Testing: Are Your Tests Any Good?

60s

Introduces a mind-blowing concept (infection testing) that challenges developers to validate their tests.

▶ Play Clip

[00:01] [Music]

[00:08] hi and welcome to the dutch php

[00:10] conference

[00:11] 2020 edition we're so glad that you've

[00:14] chosen to be here with us today we hope

[00:16] you find it a

[00:17] valuable investment of your time we've

[00:19] got a great speaker coming up for you

[00:21] but

[00:22] real quick i want to give a huge shout

[00:24] out to my buddies at ibuildings for

[00:26] putting on this conference all these

[00:28] many years

[00:28] i've been involved in this conference

[00:30] since about 2007 when i spoke at the

[00:33] one of the first ones and just

[00:35] absolutely love it it's one of my

[00:36] favorite php conferences

[00:38] ever and one of the main reasons that

[00:40] it's my favorite is that

[00:42] i buildings puts on a great conference

[00:45] so tom yoni and angela and all the

[00:48] i-building staff

[00:50] thank you all for all the hard work that

[00:52] you've put into this

[00:54] and now it is my great pleasure to

[00:57] introduce to you one of the

[00:58] my favorite people in the entire world

[01:00] this is julie or miss juliet formless

[01:02] and she's going to talk to us about my

[01:04] top 10 php unit tips and tricks juliet

[01:07] take it away

[01:08] okay so my talk is about php units but

[01:12] before we start i'm going to reiterate

[01:13] what cal said

[01:16] and that is if the slides are actually

[01:18] moving forward

[01:19] for some reason they're not ah here we

[01:22] go

[01:23] so there is a github repo where i have

[01:26] all the code samples we're going to be

[01:28] using in this in the slides

[01:30] and if you want to follow along go to

[01:33] that github repo and the commits

[01:35] which i'm referencing is in the slides

[01:38] will

[01:39] line up with that git repo and you can

[01:41] also use it obviously afterwards

[01:43] to browse and see um yeah what we talked

[01:46] about and then to take your

[01:48] benefit from that also as cal said

[01:52] please discuss it's like there's a

[01:54] specific channel for this talk

[01:56] if you want to be sure i see your

[01:58] message please place it there

[02:00] okay now to really get started

[02:04] welcome i hope you all had a nice break

[02:07] my name is juliet reindus former and

[02:09] we're going to talk

[02:10] through my top 10 php unit tips and

[02:13] tricks

[02:14] the first one being have tests

[02:19] okay how many people are behind the

[02:20] screen now with their hands

[02:22] in their uh they're facing their hands

[02:24] sinking like oh yes

[02:26] yeah i know i ought to but i haven't

[02:29] i see a few at least one hand going up

[02:32] here how

[02:33] are the responses in the chat

[02:36] cal okay cal's not responding

[02:40] okay i realize if you have a legacy code

[02:44] base

[02:45] it can be really really hard and really

[02:47] daunting to get started

[02:49] on creating unit tests for your code

[02:52] base

[02:53] do not get discouraged but start

[02:56] somewhere

[02:57] start small so the very basics to get

[03:00] started

[03:01] is to have the setup in place so to get

[03:04] the setup in place

[03:05] first we're going to adapt the composer

[03:07] json

[03:08] by adding php unit to it and then adding

[03:12] an autoload def to make sure that the

[03:14] test directory

[03:16] will autoload as well in the dev setup

[03:20] good once that's done we add a php unit

[03:24] config

[03:25] now what this does is it basically tells

[03:28] php unit

[03:29] i have a test suite named foe in

[03:32] the particular directory called tests

[03:35] every file which ends

[03:37] with test.php will be a test file and

[03:40] please execute the tests in that

[03:42] now there's two more things i want to

[03:44] draw your attention to

[03:46] first phpunit.xml.dist

[03:49] the file name there you can use the

[03:51] filename phpunit.xml or phpunit.xml.list

[03:56] i'd recommend for a project to always

[03:58] use the dist file name

[04:00] the reason for that is that people can

[04:02] then use phpunit.xml if they have

[04:04] something in their local environment

[04:06] they need to

[04:08] tweak or do have the conflict slightly

[04:10] differently

[04:11] and they can then use that file because

[04:13] it will overrule the disk file

[04:16] second thing if you only have

[04:19] a basic project where there's no

[04:21] specific environment set the setup to be

[04:24] done

[04:25] just add the fender load from composer

[04:28] as your bootstrap

[04:29] and things will work fine right

[04:32] next up you can actually get this

[04:36] generated

[04:37] largely by php unit itself

[04:41] using the generate configuration so

[04:43] that's your basics

[04:45] and then you need to start somewhere so

[04:47] start small

[04:48] find preferably a standalone function

[04:51] somewhere

[04:52] which will just be an easy starting

[04:55] point

[04:56] to get the first test in place i'm using

[04:59] this function as an example

[05:01] strip quotes basically this function

[05:04] says

[05:04] i'm getting a string i want to make sure

[05:07] that any

[05:07] surrounding quotes are stripped but only

[05:10] if they are the same type of quote

[05:12] and not if they're like somewhere within

[05:14] the string

[05:16] good so to set up that as a test i'm

[05:20] going to create a class called

[05:21] a food test where the function test

[05:24] strip quotes

[05:25] i'm gonna extend the php unit basic test

[05:29] case

[05:30] i'm gonna call my function and i'm gonna

[05:33] assert

[05:34] that the results should be the same as

[05:37] the expected results text

[05:40] your first test is in place having those

[05:44] basics in place

[05:45] makes it so that contributors know that

[05:48] tests are welcome

[05:50] the tests are desired it makes it so

[05:53] it's

[05:54] that the next person who looks at the

[05:55] code base wants to contribute doesn't

[05:57] know where to start

[05:58] knows that okay contributing test would

[06:00] be a good idea

[06:01] and maybe i should

[06:05] next use the right assertion

[06:09] hp unit has a wealth of assertions

[06:11] there's over a hundred

[06:13] assertions at my last count in php unit

[06:15] nine

[06:16] and one of the most common things i come

[06:19] across is

[06:20] people not knowing the difference

[06:22] between assert equals and the search say

[06:25] remember strict comparisons yeah assert

[06:29] equals

[06:29] is your double is searched same is the

[06:32] tripolis most of the time when people

[06:35] want to use

[06:36] cert equals they really want to use a

[06:38] search same

[06:39] because if you use strict comparisons in

[06:41] your code

[06:42] why not are why aren't you doing it in

[06:44] your unit tests

[06:48] there are a lot of assertions available

[06:50] this is just an example of

[06:53] not even the complete set of assertions

[06:56] and as you may see in the slide

[06:59] nearly every single assertion also has a

[07:01] not variant

[07:03] so assert not contained only or certain

[07:06] not contained assert

[07:07] not empty asserts not ishval

[07:12] use the right assertion and i know

[07:14] getting familiar with all these

[07:16] assertions can be hard

[07:18] so just have the manual open have the

[07:20] php unit

[07:21] manual open next to your code base

[07:24] when you're creating your unit test and

[07:26] just check if there's a

[07:27] assertion available which does what you

[07:30] need

[07:31] the reason why using the surgeons from

[07:33] php unit is

[07:34] important is one it makes your tests way

[07:37] more descriptive

[07:38] two it will make your test code a lot

[07:40] simpler because all

[07:42] of all the things php units can do for

[07:44] you which you don't have to do within

[07:45] your own test code

[07:47] and three it will generally use the

[07:50] wealth of information and wealth of

[07:52] knowledge from

[07:53] other people around the world to make

[07:54] those assertions better if they need to

[07:55] be

[07:57] right now if we look back at that first

[08:02] test we wrote let's fix that yeah

[08:05] so that assert equals should have been

[08:07] an assert same because we do want to

[08:09] make sure

[08:10] it's actually a string called text and

[08:14] that we don't get well an integer

[08:17] which will evaluate to something

[08:20] and and you know not type juggling

[08:24] and everything to do with that if you

[08:26] want to know more about that

[08:27] go and watch the big y equal doesn't

[08:29] equal quiz

[08:31] right next up don't just test the happy

[08:34] parts

[08:35] particularly people will use test driven

[08:38] development

[08:40] often have a tendency to focus

[08:44] more than anything on the happy path

[08:46] does the function

[08:47] do what i want it to do instead of also

[08:51] testing but how does it handle

[08:54] unexpected inputs

[08:55] how does it be how does this function

[08:58] behave

[08:59] in an unexpected circumstances you may

[09:02] envision that your code will only be

[09:04] used by you

[09:05] but you have no idea of the imagination

[09:07] of other people

[09:08] people will use your code in unexpected

[09:10] ways

[09:12] well unless it's a closed story in your

[09:14] own company but generally speaking

[09:16] if it's open source if the code is

[09:19] available to the public in any way

[09:21] people will use it in unexpected

[09:23] manners so don't just test the happy

[09:26] parts

[09:27] so if i look at that example function

[09:29] again let's add some more tests

[09:32] let's uh first the test with some quotes

[09:35] within the text where the quote should

[09:37] not be removed and then an unexpected

[09:40] input

[09:40] false and we document here

[09:44] that that returns an empty string it

[09:46] doesn't return the original value

[09:48] it returns an empty string so your test

[09:51] not only tests your code but also

[09:54] document the expected behavior

[09:57] now to allow for testing the unhappy

[10:00] part

[10:01] there's two things you need to remember

[10:04] do not use strict types in your

[10:06] tests and do not use parameter type

[10:08] declarations

[10:09] in your tests please do use them in your

[10:12] code

[10:13] but don't use them in your test because

[10:16] it will make

[10:16] testing the unhappy part really

[10:18] difficult because you're

[10:20] the type juggling will happen in the

[10:22] test before

[10:24] the parameters will actually get to the

[10:27] function

[10:28] under test so

[10:31] it just makes it impossible to test

[10:33] those unexpected

[10:35] cases and those uh yeah the unhappy part

[10:38] the unexpected inputs

[10:41] limit the assertions per test

[10:44] now you saw me adding two more

[10:46] assertions to that previous test

[10:49] what would happen if one of those would

[10:51] fail

[10:53] okay uh in this case

[10:56] well i know which one i felt because it

[10:58] shows me

[11:00] where the plus and the minus the actual

[11:02] expected text

[11:03] and what the actual result was however

[11:06] what happens if

[11:07] the result would be true or false value

[11:11] if you have three assertions and they

[11:12] all expect through a false

[11:15] this is not helpful you don't know which

[11:18] one of the three assertions has failed

[11:21] now every single assertion in php

[11:24] has an extra parameter and the parameter

[11:28] is called message and if we take that

[11:30] functions

[11:31] i've had the test function we had here

[11:34] we can add

[11:35] that message parameter and just tell

[11:38] phpunit this is the particular assertion

[11:41] which

[11:42] is failed you may think oh yeah but this

[11:44] is extra work

[11:46] imagine how much time you will save when

[11:48] one of these tests will start failing

[11:51] that's where it's to help you debug it's

[11:54] to make your life easier when things go

[11:56] wrong

[11:57] so if you make it a habit to always have

[12:00] to have message parameters

[12:01] if you have multiple assertions in a

[12:03] test it will just make your life easier

[12:07] now if that's a true false fails now you

[12:10] can

[12:11] see that it gives me a description of

[12:14] the test case

[12:14] and i know exactly what test case has

[12:17] failed

[12:21] yeah multiple assertions in a test okay

[12:25] in this case in my example i'm

[12:28] basically asserting the same each time

[12:31] with different input and different

[12:32] expected output i'm using the same i'm

[12:35] using the

[12:36] code in that case it's better to use a

[12:39] data provider and the data provider

[12:41] works like this

[12:43] we have the test method and we tell it

[12:46] that we're going to use a data provider

[12:48] and the name

[12:49] of the function which will be

[12:52] acting as a data provider then we

[12:56] tell it which parameters to pass to the

[12:59] test function

[13:00] and we make sure those parameters are

[13:02] used so we use

[13:03] in for calling the the actual function

[13:06] we use out as the expected

[13:09] value in your data provider

[13:12] for every single test case you want to

[13:14] test you create

[13:15] an array so you return an array of

[13:18] arrays

[13:19] where every sub-array has those

[13:22] parameters you're going to pass to the

[13:23] function

[13:28] without a data provider if one of those

[13:30] assertions we had before those three

[13:33] would fail the test would stop

[13:36] because it's failed already and the

[13:39] other

[13:41] the other assertions underneath the

[13:43] filled assertion would just not be run

[13:46] whatsoever

[13:48] with a data provider all three will run

[13:51] it will just mark the one which failed

[13:53] as failed

[13:54] but the other test cases will still be

[13:56] run

[13:57] so it makes your test more descriptive

[14:00] again and more stable

[14:03] and now okay this one should be obvious

[14:06] don't use your

[14:07] own code to create test data

[14:12] i know it should be obvious but it isn't

[14:14] always obvious to everyone

[14:16] and i'm going to read the account a

[14:18] little anecdotes with permission

[14:21] i was working with a customer on getting

[14:23] their first unit test set up

[14:26] and we discussed what we were going to

[14:28] do we discussed the function we were

[14:30] going to use it was a very

[14:32] very simple function which was

[14:35] translating

[14:35] fancy phone numbers so 800 call now

[14:39] to the real phone number

[14:43] so we discussed using data providers i

[14:46] asked the customer please

[14:47] set up some test data create you know

[14:50] some

[14:51] inputs you want to use and some outputs

[14:54] in the meantime i was setting up the

[14:55] configuration

[14:56] 10 minutes later i go and check with my

[14:59] customer how he's doing

[15:01] and yeah

[15:04] what i saw him do is he copied and

[15:06] pasted

[15:07] his own code into evol

[15:11] was running his own function in e file

[15:15] with various inputs and using the

[15:17] outputs from that as the expected data

[15:22] the function was buggy if i would have

[15:25] allowed that to continue

[15:29] he would have what the test would have

[15:31] achieved would have been to perpetuate

[15:33] the existing bugs

[15:37] instead always use an external data

[15:40] source or

[15:41] just your own brain which is also an

[15:43] external data source

[15:44] to create your test data if it's really

[15:47] complex like a really complex

[15:48] mathematical

[15:49] formula fine use an external

[15:53] site which also does that calculation

[15:55] preferably use

[15:56] two verify that both return the same

[16:00] output and then use that for your data

[16:02] provider input

[16:04] do not use your own code to create test

[16:06] data

[16:10] name your test cases now

[16:14] remember those test cases are created in

[16:16] the data provider

[16:18] if i use a feature which i really really

[16:20] like called test docs

[16:23] i can actually see which tests are being

[16:25] run in this case

[16:27] the test trick votes with dataset 0 1

[16:30] 2. now you don't want to do this when

[16:32] you run a large

[16:33] test suite but if you're debugging a

[16:35] particular test class

[16:37] this is really useful because you know

[16:38] exactly where something is going wrong

[16:40] you can see it go running but

[16:44] what if i had 70 test cases

[16:49] then okay test number 53 has

[16:52] failed okay i have to go to the data

[16:55] provider and start counting

[16:57] to figure out which one is data set 53

[17:01] that's going to cost me a lot of time

[17:03] again there's a smart solution for that

[17:05] and that's called

[17:06] named test cases so in your data

[17:09] provider

[17:11] when you have those arrays of arrays you

[17:13] can use

[17:14] the key and the key can describe what

[17:17] you're testing

[17:18] there and if you do that first of all

[17:21] your tests become more descriptive

[17:23] if you look back on your own test suite

[17:25] in two years time you will know why you

[17:27] added that particular test case

[17:29] it makes your life easier in that sense

[17:31] but also when a test

[17:33] fails it will show you in a lot more

[17:35] descriptive way

[17:36] what has failed

[17:40] see cal how are the people responding so

[17:44] far is there

[17:46] any questions so far hell

[17:51] there are no questions in the q a and

[17:53] juliet

[17:54] okay thank you joni moving on

[17:58] explore filtering now php unit has

[18:01] an incredibly powerful filtering

[18:04] mechanism

[18:05] and quite often uh obviously in your ci

[18:08] you're running your complete test suite

[18:09] but when you're developing locally you

[18:12] don't always want to have to run your

[18:14] complete test suite

[18:15] if you have 5000 tests and you're

[18:18] just tinkering with one test class you

[18:20] just want to run those tests

[18:22] because all the others won't have

[18:23] changed

[18:25] so using filtering you can do that

[18:28] and you can filter on a lot of things

[18:30] you can filter on the namespace on the

[18:31] class on the method name

[18:33] you can even filter on the test case

[18:36] number from a data provider or the name

[18:38] from a data provider

[18:40] and if this wasn't enough there's

[18:43] shortcuts to help you there as well to

[18:45] make it

[18:46] still simpler it will take a little

[18:50] getting used to

[18:51] uh figuring out what will work and what

[18:54] isn't uh won't work

[18:55] basically it's being used as a regex

[18:57] it's case insensitive

[18:59] and as i said before incredibly powerful

[19:02] especially in

[19:04] the current versions of php unit it's

[19:06] come a long way since

[19:08] php unit 45 where the filter mechanism

[19:11] wasn't as powerful

[19:12] as it is now so if i just wanted to run

[19:16] a few test cases i could filter

[19:20] not string input on the previous example

[19:24] and it will just show me the not string

[19:25] input test cases

[19:28] i hope you find this useful

[19:33] don't trust test of a code coverage but

[19:36] do examine it

[19:38] okay now code coverage can be a hotly

[19:41] debated topic

[19:43] some people will say you have to have

[19:44] 100 code coverage some people say

[19:48] i don't care about code coverage at all

[19:52] i'm somewhere in the middle but let's

[19:53] look at that function we

[19:55] used as an example if i have

[19:59] one test case this function will be 100

[20:02] covered

[20:05] however the regex adds an extra layer of

[20:08] complexity

[20:10] and even though the test coverage will

[20:12] say this test

[20:14] is a dysfunction is 100 covered in

[20:16] reality it's not

[20:18] not until i add a significant number of

[20:21] extra test cases

[20:24] now using test coverage

[20:27] is a good idea do not hang yourself with

[20:30] it do not make it a hard requirement

[20:33] but do examine it do keep track of

[20:35] what's going on

[20:36] and okay some lines aren't covered

[20:38] because they're

[20:39] for edge cases and edge cases which are

[20:42] so rare it's even

[20:43] difficult to write a unit test for it

[20:46] that's fine

[20:47] sometimes you might have private methods

[20:49] which you think uh

[20:51] hang on but those are implementation

[20:53] details so i'm not testing those because

[20:55] i'm testing them via

[20:57] other methods that's fine but do do

[21:00] something with

[21:01] code coverage do examine it to do so

[21:04] you need to tell psp unit how to record

[21:06] it

[21:07] so first of all we're going to add a

[21:09] filter

[21:11] element to the config with a white list

[21:14] and tell php units that

[21:17] all our source files are in the source

[21:19] directory

[21:20] and that is the code which needs to be

[21:22] covered

[21:24] then we're also going to tell php unit

[21:27] to lock the results

[21:28] and i'm giving as an example the clover

[21:32] logging mechanism the reason for that is

[21:35] that a lot of automated

[21:37] tooling which will integrate with your

[21:38] ci

[21:40] which you can use as status checks on

[21:42] github

[21:43] will use the clover file

[21:46] now before you say help whitelist

[21:50] each unit just like a lot of companies

[21:53] and a lot of projects in the industry

[21:55] is changing away from the term whitelist

[21:59] as of php unit 9.3 that term is

[22:02] deprecated and all these kind of

[22:05] configurations are deprecated and

[22:07] changing more

[22:09] because they're now changing the basic

[22:11] config

[22:12] expects all of the whitelist code to be

[22:16] terminology to be removed in php unit

[22:18] 10.

[22:20] if you want to have a preview look of

[22:22] what the configuration will look like

[22:25] once you move up to php unit 9.3

[22:28] there's a special sorry i'm skipping

[22:31] something

[22:32] there's a special branch in the demo

[22:34] code repo

[22:36] which you can see the commit which would

[22:38] update this configuration

[22:40] now the two things are skipped first

[22:43] force covers

[22:44] annotation okay this is about

[22:47] accidental code coverage and intentional

[22:50] code coverage

[22:51] every test method should say i'm testing

[22:55] this particular method or this

[22:56] particular class

[22:58] if you don't do that then a test which

[23:02] doesn't have a cover stack

[23:04] will just record any coverage on

[23:08] any method it uses so that would be

[23:12] accidental coverage where you're not

[23:14] targetedly testing a particular method

[23:18] by using the force covers annotation you

[23:21] make sure that

[23:22] every test has to have a cover stack and

[23:25] codes

[23:25] coverage will not be recorded unless

[23:28] there is a cover stack

[23:30] for those methods which don't

[23:32] particularly test anything but you still

[23:34] need that test

[23:35] you can use covers nothing uh

[23:39] be strict about co covers annotations

[23:42] i'm a bit in doubt in two minds about

[23:45] that one that

[23:46] is very strict it basically says that

[23:50] every single method even the ones which

[23:52] you're using under

[23:53] the hood including the ones possibly

[23:56] from dependencies

[23:58] would need to be annotated in a cover

[24:00] stack

[24:01] if you're very strict then yeah you can

[24:05] play with it but for most projects it's

[24:08] going a bit far

[24:10] so know that it's there but make up your

[24:13] own mind whether you want to use it

[24:15] so if we go back to the test method we

[24:17] had before

[24:20] we're going to add the cover stack which

[24:22] clearly indicates which method is being

[24:24] covered

[24:25] and in case you're now saying hey why

[24:27] are you using an fqn

[24:28] phpunit demands you use in fqn

[24:32] you need to have a fully qualified class

[24:34] name

[24:35] function name not doing so

[24:38] will not record the coverage properly

[24:41] so even though you say yeah i've got you

[24:43] statements no

[24:45] fully qualified name please then

[24:48] makes life easy on your contributors

[24:51] whether it's your own team or a

[24:53] team of open source contributors you

[24:55] want them to use it correctly and you

[24:57] want to

[24:58] make it easy for them to run your test

[25:00] but also run the test with code coverage

[25:02] now once you've added the coverage tags

[25:04] and add the logging

[25:06] php unit will run with code coverage

[25:08] every single time

[25:10] and that slows down your test suite

[25:12] incredibly

[25:13] so to make it easier

[25:16] add these two scripts to your composer

[25:19] file

[25:20] test with no code coverage as the

[25:23] default

[25:24] and then coverage which will record code

[25:26] coverage

[25:28] and to make life even even easier

[25:30] sometimes you're just testing something

[25:31] locally and

[25:33] you don't really want to wait for

[25:35] something to become a pr

[25:36] to have some idea of whether whether

[25:39] your changes are going to affect code

[25:41] coverage

[25:42] so make it easy to run code coverage

[25:45] and create the result locally if you

[25:48] create this script with coverage html

[25:52] php unit will generate a website which

[25:55] you can just

[25:56] open in any browser and it will show you

[25:59] the code coverage for your code

[26:04] i hope everyone by now has heard at

[26:06] least one thing which was new to them

[26:10] but if not we're not finished yet so

[26:12] don't leave without saying goodbye

[26:15] sometimes you have tests which are

[26:18] either

[26:19] uh you either you're skipping them

[26:21] because they're conditional like

[26:23] they will only run when a certain

[26:24] extension is available

[26:26] or they will should only be run on a

[26:29] certain php version

[26:30] or you might have test stops where

[26:34] you're

[26:34] basically just go skeleton method

[26:38] we need a test here but it's not there

[26:40] yet

[26:41] an incomplete test now

[26:44] for a conditional test you can do it

[26:47] like this you can just have

[26:49] a comparison and return before doing an

[26:52] assertion

[26:55] that test will be marked as risky by php

[26:58] unit because it's a test without

[26:59] assertion

[27:00] if you run it on php 72 we can do better

[27:07] we can actually tell phpunit to mark the

[27:10] test as skipped

[27:11] then in that little dot overview which

[27:13] you have as the default

[27:15] test runner you'll get an s that test is

[27:18] skipped

[27:19] but then you still don't know why the

[27:22] test is skipped

[27:23] so document that by adding a message

[27:26] this test requires php 73

[27:30] and the example code is in in the test

[27:33] branch

[27:35] is a bit nonsense but it's just to drive

[27:37] the point home

[27:38] what the different options are in this

[27:40] case if

[27:41] we now run the test again we get the s

[27:44] for skipped

[27:45] but if we run php unit in for bose mode

[27:48] it will also list

[27:49] all the skip tests including the reason

[27:52] they were skipped

[27:54] now psp unit has a number of built-in

[27:58] checks there so for php version or a pc

[28:03] unit version or an extension or a class

[28:05] exist or function exist

[28:07] you can actually use annotations

[28:10] and it will do the same thing and you

[28:11] don't even have to do the version

[28:12] compare yourself anymore

[28:15] and again php unit will

[28:19] annotate that with information and mode

[28:21] with why the test was skipped

[28:26] right now and one where when i

[28:29] previously gave this test i got

[28:31] responses on twitter from people seeing

[28:32] the slide saying like i've now made my

[28:34] test

[28:35] cross version compatible

[28:40] sometimes when you have a project you

[28:42] can determine freely what the minimum

[28:44] php version is

[28:45] and you can say okay seven two seven

[28:47] three

[28:48] fine however especially with open source

[28:52] or

[28:53] with projects which work with legacy

[28:55] code

[28:56] so upgrading might be difficult and

[28:59] changing the minimum required version

[29:01] might be a slow process

[29:05] if we look at the versions of php unit

[29:07] itself

[29:08] you can see that of the currently

[29:11] supported versions

[29:13] uh which are php unit eight and nine

[29:16] php seven two sorry

[29:20] my clicker is gone php72 is the minimum

[29:24] uh and with php 7.2 as a minimum

[29:29] yeah if you still need to be uh support

[29:31] php 7

[29:33] that's going to be difficult because

[29:37] in php unit 8 php unit

[29:40] introduced void return type for any of

[29:43] the fixtures

[29:45] and by the fixtures i mean things like

[29:48] uh you use this particular type of

[29:51] functions called

[29:52] setup before class tear down after class

[29:54] setup

[29:55] and a teardown set before class and ah

[29:58] tier that after class are

[29:59] for setting up things which are used by

[30:01] every single tester

[30:03] and setup and teardown are run before

[30:06] and efforts

[30:07] and after every single test

[30:10] with the void return type dead void

[30:12] return type wasn't introduced until php

[30:15] 7-1 so if you still need to support php

[30:18] 7-0 and therefore also need to support

[30:21] php

[30:22] unit 6 you have a problem

[30:25] and i'm going to click through to the

[30:27] next slide and look if i can find

[30:29] a little bit from to get my clicker

[30:32] working in

[30:33] i don't think i can so in the meantime

[30:36] let's have a look how we can do this

[30:39] as of php unit 8 we need the void return

[30:42] type

[30:44] this is a problem so what to do to get

[30:47] rounded first we rename the

[30:49] method

[30:52] to setup database connection because

[30:55] that's what it does

[30:56] it makes the method far more descriptive

[30:59] than setup before class anyway

[31:01] and we add the before class annotation

[31:05] with the before class annotation it will

[31:08] run the method as if it was set up

[31:10] before class

[31:11] and you do not have to have the return

[31:13] type for it

[31:14] same goes for after you can use

[31:17] after class rename the method don't use

[31:20] the return type for it

[31:22] and similar for setup and for teardown

[31:25] which are the methods

[31:26] run before and after every test again

[31:29] you have the before

[31:30] and after annotation you've seen me use

[31:34] several annotations now you've seen me

[31:36] use

[31:36] annotations for coverage you've seen me

[31:39] use these annotations you've seen me use

[31:41] the requires annotation

[31:42] there are more please have a good look

[31:45] at those

[31:46] because annotations can be really

[31:47] powerful in php units

[31:49] and before you ask yes

[31:53] these annotations before class after

[31:55] class before and after

[31:57] will stay supported at least for the

[31:59] foreseeable future

[32:01] i spoke with sebastian and he's

[32:03] indicated he has no intention

[32:05] in the foreseeable future to drop

[32:07] support for them so this should make

[32:09] your

[32:09] test suite cross-version compatible with

[32:12] various php unit versions

[32:14] and still maintainable for the future

[32:19] okay test your tests now this might

[32:22] sound

[32:23] really really matter and in a way it is

[32:26] but are your tests actually testing what

[32:29] they're supposed to do

[32:31] to test what are they stable what if

[32:34] something changes in your code base will

[32:35] you start a test start failing

[32:37] uh straight away and

[32:40] in some cases will they not fill when

[32:43] it's something like a constant which

[32:45] change

[32:47] should the test maybe take that constant

[32:48] into account

[32:50] so to help with that first of all

[32:53] we have infection infection is mutation

[32:57] testing

[32:57] it's a framework which will try

[33:00] to mutate your source code and then

[33:05] see if your tests fill correctly on the

[33:07] mutated code

[33:10] if you want to learn more about it first

[33:11] of all there's a url to their own

[33:13] website but second of all

[33:15] there's a youtube video from my dear

[33:17] friend michelangelo van damme

[33:19] where he gives a little demo of how

[33:20] infection works

[33:22] i'd recommend watching it and i know

[33:24] he's given more talks on it if you

[33:26] have a chance to see it uh go and watch

[33:29] michelangelo from them talk about the

[33:30] election it's worth your time

[33:33] another thing and this is just a quick

[33:35] refill because they're not

[33:36] online yet php unit compatibility and

[33:40] php unit qa

[33:42] these will be rule sets for php code

[33:44] sniff so they will integrate

[33:46] in your coding standard check which you

[33:48] do by default anyway

[33:50] don't you and

[33:54] these will the qa one will check things

[33:57] like

[33:58] hey you should use a different assertion

[34:00] or there's a more specific assertion you

[34:02] could use for this particular

[34:03] test code php unit compatibility will

[34:06] warn you just like php unit itself does

[34:09] in verbose mode

[34:11] about using certain things which are

[34:13] deprecated

[34:14] except php unit will do it when you're

[34:18] basically already

[34:19] too late and with this you can do it

[34:22] using an

[34:22] or an older php version to figure out

[34:25] what is needed to upgrade

[34:27] your test suite so expect those soon

[34:33] last tip lose count

[34:37] i think i said at the beginning this was

[34:38] a top 10.

[34:40] please follow example lose count

[34:43] you can never have enough tests having

[34:46] some extra tests even though the really

[34:48] edge cases and you think those will

[34:50] never ever happen in real life can be

[34:53] helpful

[34:54] uh also remember things change

[34:58] in the world whether it's in your

[34:59] dependency or whether

[35:01] php8 which is around the corner comes

[35:04] along

[35:05] and changes all sorts of notices and

[35:07] warnings to

[35:08] errors and exceptions make sure you test

[35:12] those edge cases

[35:14] because then your test suite will be

[35:15] able to catch those situations

[35:18] of php cross-version compatibility or

[35:20] changes in your dependency

[35:22] before it gets to production so it's not

[35:25] a bad thing to have some extra

[35:27] test cases right

[35:30] that was it for me i hope you enjoyed

[35:33] this talk i hope you found something

[35:35] useful in it

[35:36] please leave feedback joined in you can

[35:39] use qr code on the slide

[35:41] to get straight to the right page the

[35:44] slide will be up later this afternoon at

[35:46] speaker deck

[35:47] and yeah of course

[35:51] read the docs from phpunit

[35:54] cal are you there now yes you're there

[35:59] and we have a question please

[36:02] um about the usage of code to generate

[36:05] test data what is your opinion on using

[36:07] constants from the code on test

[36:14] come again about the usage of

[36:18] code to generate test data what is your

[36:21] opinion on using constants from the code

[36:24] on tests

[36:25] i tend to duplicate the constants in the

[36:27] test to ensure the changes in the

[36:29] constant values are detected

[36:31] but some colleagues disagree with this

[36:34] okay it depends on the context and i

[36:38] can't give you

[36:39] a hundred percent answer because it

[36:41] really depends on the code

[36:42] there are in my own code basis

[36:46] i have situations where i'm

[36:49] duplicating the constants just to make

[36:52] sure that if the constant

[36:54] changes the test will fill and i know

[36:56] something has changed

[36:58] uh there's also places where i use the

[37:02] actual constant from the source code

[37:05] because it's a public constant in this

[37:07] case

[37:08] in my test to make sure that the test

[37:12] will

[37:12] continue to pass even when the constant

[37:15] changes

[37:16] so it depends on the context and you

[37:19] really need to look at the individual

[37:20] situation for that

[37:23] excellent i know it's not yeah it's it's

[37:25] not a yes or no answer i'm sorry

[37:30] that's okay it was an answer so hey

[37:33] um let's see i think that's the only

[37:36] question we had for you

[37:37] so i am going to thank you for that i

[37:41] appreciate that that was a wonderful

[37:43] presentation hope everybody got as much

[37:45] out of it as i did

[37:46] okay um let's see let me

[37:50] take back over the screen and share that

[37:55] okay we got 20 minutes until the next

[37:57] talk 20 minutes so get up get stretch

[38:00] get you a cup or a glass of whatever

[38:03] you're drinking for the afternoon

[38:05] and we'll be back at the top of the hour

[38:07] hey don't forget go out to joined in

[38:09] leave juliet some feedback on what she

[38:11] did right and what she can do better

[38:14] and of course we always appreciate it

[38:15] when you tweet out

[38:17] or whatever social media you use when

[38:19] you post about

[38:20] dpc20 thank you we'll be back in 20

[38:23] minutes

[38:24] see you later

[38:34] you

⚡ Saved you 0h 38m reading this? Transcribe any YouTube video for free — no signup needed.