1. Introduction
Perhaps you are not too strange with testing code anymore. In the past, I thought that testing was the job of QA’s side, and I thought that the code I ran up to was simple, then they found some exceptions, or some of the functions that ran wrong with that initial estimate were called tests. But when I go to work, I know that after each line of your code, it must ensure some properties, and will have to ensure some strict requirements, if you do not want to have a nice day. it rolls off dead and you find eye strain don’t know why. Or develop an old code feature and don’t understand why it reports bugs. To overcome this situation laravel has provided us with 2 types of Testing: UnitTest and FeatureTest. In this article, I will focus on the simplest concepts and tasks that UnitTest performs.
2. What is UnitTest
What is PHPUnit : You can simply understand that the php unit will go through each line of your code, each part calling repositories … to make sure what you are doing. It supports most PHP frameworks including Laravel.
In addition to testing the quality of the code, I think it is great for you to control what you have written, such as the type of data returned, or through each step how it will access and return. about what. It will help you dig deeper into the code relatively much
3. Test folder structure
In here you will see relatively many files but you only need to care about the main parts:
1: tests / Feature contains the code used for FeatureTest
2: tests / Unit contains the code used for UnitTest
3: TestCase : is a bootstrap file to set up Laravel environment for tests
4: phpunit.xml is the configuration file for PHPUnit
4. Syntax for creating a test
To create a test, use the command:
1 2 3 4 5 |
// Tạo 1 test trong thư mục Feature php artisan make:test UserTest // Tạo 1 test trong thư mục Unit php artisan make:test UserTest --unit |
5. Some test examples
I am using packit, to support writing tests
1 2 |
<span class="token keyword">use</span> <span class="token package">Tests TestCase</span> <span class="token punctuation">;</span> |
To run the test you just wrote you can run the command
1 2 |
vendor <span class="token operator">/</span> bin <span class="token operator">/</span> phpunit <span class="token operator">--</span> filter <span class="token operator">=</span> test_it_can_show <span class="token comment">//với filte để chạy function mình đang test</span> |
You notice the line :
1 2 |
<span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">controllerMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">index</span> <span class="token punctuation">(</span> <span class="token variable">$request</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
It will point to the exact function you are testing. Without this part, you will not be able to test what you need.
With a normal controller, you will see a part of construct, how to test this segment:
1 2 3 4 5 6 7 |
<span class="token keyword">protected</span> <span class="token variable">$mediaRepository</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct</span> <span class="token punctuation">(</span> MediaRepositoryIf <span class="token variable">$mediaRepository</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">mediaRepository</span> <span class="token operator">=</span> <span class="token variable">$mediaRepository</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
With the above code, I will write the following test:
1 2 3 4 5 6 7 8 9 10 11 |
<span class="token keyword">protected</span> <span class="token variable">$mediaRepositoryTest</span> <span class="token punctuation">;</span> <span class="token keyword">protected</span> <span class="token variable">$controllerMock</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">setUp</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> void <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">afterApplicationCreated</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">mediaRepositoryTest</span> <span class="token operator">=</span> Mockery <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> MediaRepositoryIf <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">controllerMock</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UploadMediaController</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">mediaRepositoryTest</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">parent</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">setUp</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
You can understand it simply at $this->afterApplicationCreated
calling controller and setting up global variables or, initializing some variables that you see repeatedly.
For example, because I check rights policies, middleware so every time I want to access a controller I will need to log in. instead of each function test I have to retype the login, I can write straight on $this->afterApplicationCreated
once and use it as follows.
1 2 3 4 5 |
<span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span> <span class="token punctuation">;</span> <span class="token variable">$user</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">id</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> <span class="token variable">$user</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">role</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'admin'</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">actingAs</span> <span class="token punctuation">(</span> <span class="token variable">$user</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
I think usually with normal controllers you will surely have input requests, so how to test these requests:
1 2 3 |
<span class="token variable">$request</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">headers</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'content-type'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'application/json'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
In case you have additional parameters to pass, you can add the following:
1 2 3 4 5 |
<span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">initialize</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'user_id'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'role_id'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Ok, go to a normal test controller:
1 2 3 4 5 6 7 8 9 10 |
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">index</span> <span class="token punctuation">(</span> Request <span class="token variable">$request</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">user</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$media</span> <span class="token operator">=</span> <span class="token variable">$user</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">media</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">response</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'data'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token variable">$media</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
With the simple code above, I will have the following test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<span class="token keyword">use</span> <span class="token package">Mockery</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">test_it_can_index_upload_media</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$request</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">headers</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'content-type'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'application/json'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$hasManyMock</span> <span class="token operator">=</span> Mockery <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> HasMany <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$userMock</span> <span class="token operator">=</span> Mockery <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> User <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">makePartial</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">setUserResolver</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">use</span> <span class="token punctuation">(</span> <span class="token variable">$userMock</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$userMock</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$userMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">shouldReceive</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'media'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">andReturn</span> <span class="token punctuation">(</span> <span class="token variable">$hasManyMock</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$hasManyMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">shouldReceive</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'get'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">andReturn</span> <span class="token punctuation">(</span> <span class="token function">response</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">controllerMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">index</span> <span class="token punctuation">(</span> <span class="token variable">$request</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">assertInstanceOf</span> <span class="token punctuation">(</span> JsonResponse <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">,</span> <span class="token variable">$response</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Now I will explain to you to understand how the above code is running:
with paragraph:
1 2 |
<span class="token variable">$user</span> <span class="token operator">=</span> <span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">user</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
This means that your request is logged in and contains the user information. To test the above code, I have the following code:
1 2 3 4 |
<span class="token variable">$request</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">setUserResolver</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">use</span> <span class="token punctuation">(</span> <span class="token variable">$userMock</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$userMock</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Next I have a paragraph
1 2 |
<span class="token variable">$user</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">media</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
So this $user->media()
will return relationships of Models User to Models Media , my hasMany will have a test:
1 2 3 |
<span class="token variable">$hasManyMock</span> <span class="token operator">=</span> Mockery <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> HasMany <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$userMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">shouldReceive</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'media'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">andReturn</span> <span class="token punctuation">(</span> <span class="token variable">$hasManyMock</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
then ->get()
, it will return a list in the media table that satisfies the condition, so I will return an array of data. But you can see our code is expecting a return
1 2 3 4 |
<span class="token function">response</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'data'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token variable">$media</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
So I have the test:
1 2 |
<span class="token variable">$hasManyMock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">shouldReceive</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'get'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">andReturn</span> <span class="token punctuation">(</span> <span class="token function">response</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Last sentence:
1 2 |
<span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">assertInstanceOf</span> <span class="token punctuation">(</span> JsonResponse <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">,</span> <span class="token variable">$response</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
implying that you are checking whether the returned data type is true for the data you need to test or not.
6. How do you know your UnitTest is satisfactory?
We write a satisfactory unittest when all three of the following are met:
- Arrange: state setting, Object initialization, mock mock up
- Act: Run the unit that is being tested
- Assert: Compare expected with the returned result.
7. Conclusion
By reading this, you probably have an idea of unit tests and some basic functions in unit tests and how to use them correctly. Thank you for reading my article, if you have any questions, or if you have any part that you do not understand, you can comment below the comment section so that we can learn better. And especially do not forget to give me a like and a share !