So we have gone through 2 parts on Unit Test, in this section I will show you how Mock and Mock’s application to test a program.
What is mocking?
Mocking is a technique that helps us initialize a virtual object of a real class. During the test, we only use certain methods of an object but do not want it to perform, instead just want it to be called and get a value. I gave an example that will help you understand what that sentence means.
1 2 3 4 5 6 | <span class="token keyword">function</span> <span class="token function">showValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token function">getValueFromDatabase</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 variable">$value</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Following this example we can see that the showValue () method will call the database to display the data. When performing this method test, we do not want to have to connect to any database. The connection will make the test speed significantly decrease, besides, if not configured correctly you can completely falsify the database you are using. In these cases, the best is to “fake” the getValueFromDatabase () method and fake the data returned from that method. This helps us ensure that the code will not call the database but the data will be consistent after the tests. And that is Mocking .
We can understand that Mocking helps us create test data in the format we want, besides controlling the operating data flow. Thanks to that, test data will not have to be stored as retrieved from the database. In addition, Mocking creates mock objects that do not need to function as a real object, only the methods or properties that we want to use in the test.
There are 5 types of Mocking, each of which will be used in specific cases:
- Dummy Object
- Test Stubs
- Test Spies
- Test Mocks
- Test Fakes
Dummies
Dummy objects are objects that are passed into methods but are not used. Typically, they are used to pass methods to give the necessary parameters for a function to work.
Spies
Test Spies is an object to investigate the interaction between objects. When the result of a test case depends on whether a method is interacting as you want, the number of times the method is called, the number of parameters passed is correct. ..
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token comment">// arrange</span> <span class="token variable">$spy</span> <span class="token operator">=</span> <span class="token package">Mockery</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">spy</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'MyDependency'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$sut</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyClass</span> <span class="token punctuation">(</span> <span class="token variable">$spy</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// act</span> <span class="token variable">$sut</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">callFoo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// assert</span> <span class="token variable">$spy</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">shouldHaveReceived</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">foo</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">with</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'bar'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
As an example, we only check if MyDependency is called and passed a bar parameter.
Stubs
Test stubs is to create an object with pre-programmed parameters. Typically, stubs will return values that will be hard-coded into.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">function</span> <span class="token function">getName</span> <span class="token punctuation">(</span> Stubs <span class="token variable">$s</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$s</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">name</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">test</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$testStubs</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stubs</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$testStubs</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">name</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'Viblo'</span> <span class="token punctuation">;</span> <span class="token comment">// Sẽ pass </span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">assertEquals</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'Viblo'</span> <span class="token punctuation">,</span> <span class="token function">getName</span> <span class="token punctuation">(</span> <span class="token variable">$testStubs</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Fake
Fake test is an object that is simplified and can be manipulated almost like the objects we interact with in “production”. This is a relatively abstract concept with different interpretations. As an example we can see, to test the login function, we must use a Login Service. Instead of having to connect to the database or send requests to a third party, we create a fake service that has a database stored in temporary memory with methods like the real service but returns fake values to We can perform tests. Fake can be considered an advanced form of Stubs when it not only returns objects with parameters that we require that we can interact with what is in “production”.
Mock
When people talk about Mock, they often refer to Test Doubles. Test Doubles is a term that refers to how you replace an object on production for testing. This object is both used for testing itself as well as making the total method workable from which we can test.
A Mock Object actually runs when its methods are called. It can throw the corresponding exceptions if they are called without being called or check if the number of parameters passed is exactly what they expected.
We use Mock when testing a function with multiple parameters related to each other and each parameter will operate separately and return the corresponding values.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token variable">$mock</span> <span class="token operator">=</span> <span class="token package">Mockery</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'MyClass'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$mock</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">'foo'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">withArgs</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token variable">$arg</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token variable">$arg</span> <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">true</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</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">$mock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// matches the expectation</span> <span class="token variable">$mock</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token number">3</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// throws a NoMatchingExpectationException</span> |
Mock in Laravel
Although PHPUnit can also create Mock Objects but does not provide enough functionality like Mockery Fortunately Laravel will preinstall Mockery for you so we do not need to install or configure it, you can start to do it. Job.
First we need a sample code to be able to use it. We will use two classes, Math and Calculate. Math class will depend and Calucule. Or create a file with the path “app / Math.php”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">App</span> <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Math</span> <span class="token punctuation">{</span> <span class="token comment">/** * @var Calculate */</span> <span class="token keyword">public</span> <span class="token variable">$calculate</span> <span class="token punctuation">;</span> <span class="token comment">/** * Math constructor. * * @param Calculate $calculate */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct</span> <span class="token punctuation">(</span> Calculate <span class="token variable">$calculate</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">calculate</span> <span class="token operator">=</span> <span class="token variable">$calculate</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Get Area * * @param $length * * @return float|int */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">getArea</span> <span class="token punctuation">(</span> <span class="token variable">$length</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">calculate</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">areaOfSquare</span> <span class="token punctuation">(</span> <span class="token variable">$length</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">App</span> <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Calculate</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">areaOfSquare</span> <span class="token punctuation">(</span> <span class="token variable">$length</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">pow</span> <span class="token punctuation">(</span> <span class="token variable">$length</span> <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Note: When writing code, always use dependency injections via contructors instead of initializing them with the “new” syntax. This will help us more easily test when creating mock.
Another small tip is to always use Facade instead of the Laravel function helper, which will help us more or less in that test. https://laravel.com/docs/master/mocking#mocking-facades
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Tests Unit</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App Math</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Mockery</span> <span class="token keyword">as</span> m <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Tests TestCase</span> <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">MathTest</span> <span class="token keyword">extends</span> <span class="token class-name">TestCase</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> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">calculate</span> <span class="token operator">=</span> m <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">mock</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'AppCalculate'</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">math</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Math</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">calculate</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">test_getArea_WhenCalledWithLength2_Return4</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">calculate</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">'areaOfSquare'</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 number">4</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">once</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$length</span> <span class="token operator">=</span> <span class="token number">2</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">math</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">getArea</span> <span class="token punctuation">(</span> <span class="token variable">$length</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">assertTrue</span> <span class="token punctuation">(</span> <span class="token function">is_int</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> <span class="token variable">$this</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">assertEquals</span> <span class="token punctuation">(</span> <span class="token number">4</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> <span class="token punctuation">}</span> </span> |
Test run in terminator:
So we test and succeed. But why we have two assertion functions, assertTrue and assertEquals, which we get 3 in the result?
Another assertion is shouldReceive . As I mentioned above, this is Test Double. We have just verified that the areaOfSquare method in the Calculate class is called, and has assigned the returned data so we can test the complete test case test_getArea_WhenCalledWithLength2_Return4 .
Not only that, we also check that the areaOfSquare method is called only once in getArea . If we replace once with twice or times ({number}) then the test case will get an error.
There are also some techniques and some Expectation functions that you refer to in the docs of Mockery: http://docs.mockery.io/en/latest/reference/expectations.html
summary
That’s the end of a series of instructions to write Unit Test for my Laravel. If you have any questions or concerns, please comment below