Download presentation
Presentation is loading. Please wait.
1
DRUPAL CAMP NJ 2018 DRUPALCAMPNJ
2
DRUPAL CAMP NJ 2018 PHPUnit testing in Drupal 8.
3
PHPUnit testing in Drupal 8.
Sugandh Khanna Srijan, INDIA Drupal Camp NJ March 2018
4
AGENDA Automated testing in D8 - History Type of tests in D8
What is Unit Testing Why we need unit testing What is PHPUnit Php unit test -Best practices & Thumb rule A Basic example How to run test Assertions & data Providers Test doubles Setup() Method Mock Objects Stub Methods
5
AUTOMATED TESTING IN DRUPAL - HISTORY
Drupal 6 - Simpletest module Drupal 7 - Simpletest in core Drupal 8 - Simpletest still in core but deprecated (to be removed in D9) PHPUnit introduced into core This presentation focuses exclusively on PHPUnit
6
TYPES OF TESTS IN DRUPAL 8
Unit tests Kernel tests Functional tests JavaScript functional tests
7
Types of testing with an oversimplified example:
For a functional mobile phone, the main parts required are “battery” and “sim card”. Unit testing– the battery is checked for its life, capacity and other parameters. Sim card is checked for its activation. Kernel Testing– battery and sim card are integrated i.e. assembled in order to start the mobile phone. Functional Testing– the functionality of the mobile phone is checked in terms of its features and also battery usage as well as sim card facilities.
8
Exploring Unit testing only...
A unit test, then, is a sanity check to make sure that the chunk of functionality does what it’s supposed to. As a client, unit testing is not something you will see in an end product—it’s a type of testing performed during the coding stage. You can always specify that a potential developer you hire uses unit testing during the development phase to ensure your product has reduced logical bugs. Without getting too technical, unit testing is a form of coding that breaks your software down into specific functions then tests each individually for any logic flaws. The goal is to find bugs before your customers find them.
9
Few important points about unit testing and its benefits:
Unit testing is done before Integration testing using white box testing techniques. Unit testing does not only check the positive behavior but also the failures that occur with invalid input. Finding issues/bugs at an early stage is very useful and it reduces the overall project costs. A unit test tests small pieces of code or individual functions so the issues/errors found in these test cases are independent and do not impact the other test cases. Another important advantage is that the unit test cases simplify and make testing of code easier. Unit test saves time and cost, and it is reusable and easy to maintain. Unit testing is done before Integration testing using white box testing techniques. Unit testing does not only check the positive behavior i.e. the correct output in case of valid input, but also the failures that occur with invalid input. Finding issues/bugs at an early stage is very useful and it reduces the overall project costs. As Unit testing is done before integration of code, issues found at this stage can be resolved very easily and their impact is also very less. A unit test tests small pieces of code or individual functions so the issues/errors found in these test cases are independent and do not impact the other test cases. Another important advantage is that the unit test cases simplify and make testing of code easier. So, it becomes easier to resolve issues at a later stage too because only the latest change in the code is to be tested. Unit test saves time and cost, and it is reusable and easy to maintain. Coming up: why we need unit testing? Importance of unit testing.
10
Why we need unit testing?
Unit testing any application will not only save you a lot of headaches during development, but it can result in code that’s easier to maintain, allowing you to make more fearless changes (like major refactoring) without hesitation.
11
Let’s begin...
12
What is PHPUnit? A unit testing framework written in PHP, created by Sebastian Bergman Part of the xUnit family of testing frameworks. While there are other testing frameworks for PHP (such as SimpleTest or Atoum) PHPUnit has become the de facto standard.
13
Best practice for writing PHPUnit tests
14
File structure and filenames
Unit tests go in [MODULENAME]/tests/src/Unit Namespace is Drupal\Tests\[MODULENAME]\Unit
15
Classnames: This should be exactly same as filenames.
Method (test) names: Our test method names should start with test, in lower case. Extends PHPUnit: Classes must extend the class, UnitTestCase or extend a class that does
16
Thumb rule: Before writing the first test, think about what we need to actually test from the code given.
17
Basic Example <?php <?php
namespace Drupal\Tests\vf_hierarchy\Unit; use Drupal\Tests\UnitTestCase; /** vf_hierarchy */ class HierarchyControllerTest extends UnitTestCase { public function testDummyFunc() { $foo = true; $this->assertTrue($foo); } <?php namespace Drupal\vf_hierarchy\Controller; use Drupal\Core\Controller\ControllerBase; class HierarchyController extends ControllerBase { public function dummyFunc() { // } ??
18
ASSERTIONS What is an assertion? Wikipedia defines an assertion as a predicate (a true–false statement) placed in a program to indicate that the developer thinks that the predicate is always true at that place. Translated, all it is saying is that an assertion verifies a statement made equals true.
19
In order to run this test… Either
Enable Testing module. Php core /scripts/run-tests.sh --verbose --url localhost --color vf_hierarchy
21
Processing test 1 0f 1 - Drupal\Tests\vf_hierarchy\Unit\HierarchyControllerTest
23
This method will turn a string into a URL-safe string: "This string will be sluggified" will turn into "this-string-will-be-sluggified".
26
What are Test Doubles?
27
Test doubles (or mock objects) allow to focus a unit test on the code that needs to be tested without bootstrapping dependencies. a function of one class is calling another class's function - resulting dependency in two classes.
28
When to use test double?
29
It is very common in our code, a function of one class is calling another class's function. In this case, we have a dependency in these two classes. In particular, the caller class has a dependency on calling class. But as we already know, unit test should test the smallest unit of functionality, in this case, it should test only the caller function. To solve this problem, We can use test double to replace the calling class. Since a test double can be configured to return predefined results, we can focus on testing the caller function.
30
Types of “Test Doubles”
31
Types of test doubles Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production. Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Spies are stubs that also record some information based on how they were called. One form of this might be an service that records how many messages it was sent. Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.
32
Setup(), Mock Objects, data Providers and Stub Methods
33
<?php namespace Drupal\vf_device_models\Controller; use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; class DeviceModelController extends ControllerBase { public function getDeviceModel(Request $request, $id = NULL, $name, $level) { $result = ' ‘; $result = $this->getDeviceModelLists($request, $id, $name, NULL, $level); $response = new JsonResponse($result); If ($response) { return TRUE; }
34
Weather the function is returning json object or not?
Before writing the first test, think about what we need to actually test from the code given. Check….. Weather the function is returning json object or not? If there’s is json object, asserts true, else test will fail. Let’s prepare skelton:
35
<?php namespace Drupal\Tests\vf_device_models\Unit { use Drupal\vf_device_models\Controller\DeviceModelController; use Drupal\Tests\UnitTestCase; /** vf_device_models */ class DeviceModelControllerTest extends UnitTestCase { protected $messenger; protected $request; protected function setUp() { parent::setUp(); $this->messenger = new DeviceModelController(); } public function testgetDeviceModel() { //body of function…….. ??
36
• The setUp method is executed for every test method in a class.
protected function setUp() { $this->stack = []; } PHPUnit: setUp • The setUp method is executed for every test method in a class. • Configure fixtures or setting up known state such as database or test files. • Configure test doubles or mocks, which are dependencies of a unit that you do not need to test in that test class. Invoked before a test method is run. One of the most time-consuming parts of writing tests is writing the code to set the world up in a known state and then return it to its original state when the test is complete. This known state is called the fixture of the test.
37
protected function setUp() {
parent::setUp(); $this->messenger = new DeviceModelController(); } public function testgetDeviceModel() { $result = $this->messenger->getDeviceModel($this->request,'1209','sugandh25062','market','514'); $this->assertTrue($this->messenger->getDeviceModel($this->request,'1209','sugandh25062','market','514'), "The json object is created.");
39
MOCK Why this exception????? Because it requires “Request” instance for the test function to execute. Since unit testing means test in isolation, so the need of the hour is to create a fake object of class Request.
40
$this->getMockBuilder('\Symfony\Component\HttpFoundation\Request')
protected function setUp() { parent::setUp(); $this->request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request') ->disableOriginalConstructor() ->setMethods([ ]) ->getMock(); $this->messenger = new DeviceModelController($this->request); }
42
Happy! First test run success…. :D
43
Let’s have one more example from core:
So understood Mocks? Or need 1 more example? Let’s have one more example from core: (Function checks whether user is authenticated or not.. If yes, returns uid.)
44
public function authenticate($username, $password) {
$uid = FALSE; if (!empty($username) && strlen($password) > 0) { $account_search = $this->entityManager->getStorage('user')->loadByProperties(['name' => $username]); ..... } return $uid;
45
/** providerTestAuthenticateWithMissingCredentials */ public function testAuthenticateWithMissingCredentials($username, $password) { $this->userStorage->expects($this->never()) ->method('loadByProperties'); $this->assertFalse($this->userAuth->authenticate($username, $password)); } * Data provider for testAuthenticateWithMissingCredentials(). public function providerTestAuthenticateWithMissingCredentials() { return [ [NULL, NULL], [NULL, ''], ['', NULL], ['', ''], ]; Data providers .. ??
46
PHPUnit: Data Providers
• A data provider is a method that returns an array of parameters to pass into a test method. • A test method may test several inputs. • Important to note that data provider methods are run before any setup so this cannot depend on any test doubles.
47
protected function setUp() {
$this->userStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); $entity_manager->expects($this->any()) ->method('getStorage') ->with('user') ->will($this->returnValue($this->userStorage)); $this->passwordService = $this->getMock('Drupal\Core\Password\PasswordInterface'); $this->testUser = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->setMethods(['id', 'setPassword', 'save', 'getPassword']) ->getMock(); $this->userAuth = new UserAuth($entity_manager, $this->passwordService); } Coming up: Definition of mock.
48
make a replica or imitation of something.
Mock - Type of test make a replica or imitation of something. or faking the behavior of object Coming up: Types of mocks
49
Mock Methods getMock() and getMockBuilder()
Coming up: difference bw methods.
50
when the defaults used by the getMock() method to generate the test double do not match your needs then you can use the getMockBuilder($type) method to customize the test double generation using a fluent interface. Here is a list of methods provided by the Mock Builder: setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced. setConstructorArgs(array $args) can be called to provide a parameter array that is passed to the original class' constructor (which is not replaced with a dummy implementation by default). setMockClassName($name) can be used to specify a class name for the generated test double class. disableOriginalConstructor() can be used to disable the call to the original class' constructor. disableOriginalClone() can be used to disable the call to the original class' clone constructor. disableAutoload() can be used to disable __autoload() during the generation of the test double class.
51
STUB The practice of replacing an object with a test double that (optionally) returns configured return values is referred to as stubbing.
52
THE FOUR PATHWAYS OF GETMOCKBUILDER()
53
Do not call setMethods()
This is the simplest way: $this->testUser = $this->getMockBuilder('Drupal\user\Entity\User') ->getMock(); This produces a mock object where the methods Are all stubs, All return null by default, Are easily overridable
54
Passing an empty array You can pass an empty array to setMethods():
$this->testUser = $this->getMockBuilder('Drupal\user\Entity\User') ->getMock(); ->setMethods(array()) This produces a mock object that is exactly the same as if you have not called setMethods() at all. The methods Are all stubs, All return null by default, Are easily overridable
55
Passing null You can also pass null:
$this->testUser = $this->getMockBuilder('Drupal\user\Entity\User') ->setMethods(null) ->getMock(); This produces a mock object where the methods Are all mocks, Run the actual code contained within the method when called, Do not allow you to override the return value
56
Passing an array containing method names
$this->testUser = $this->getMockBuilder('Drupal\user\Entity\User') ->setMethods(array(‘id', 'setPassword', 'save', 'getPassword’)) ->getMock(); This produces a mock object whose methods are a mix of the above three scenarios.
57
The methods you have identified
Are all stubs, All return null by default, Are easily overridable Methods you did not identify Are all mocks, Run the actual code contained within the method when called, Do not allow you to override the return value
58
This means that in the $this->testUser mock object the ‘id()', 'setPassword()', 'save()' and 'getPassword()’ methods would return null or you can override their return values, but any method within that class other than those four will run their original code.
59
If you compare it to debugging:
Stub is like making sure a method returns the correct value Mock is like actually stepping into the method and making sure everything inside is correct before returning the correct value.
60
Any Questions?
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.