Drupal

uFavicon

New Drupal Modules - 15 October 2019 - 10:48pm

This module allows the user to have its own favicon same as "User Picture".

Categories: Drupal

JSON:API Resources

New Drupal Modules - 15 October 2019 - 6:09pm

This module allows you define custom resources at routes of your choice that use existing resource types. This is useful for defining routes that return resource objects based on context like the currently authenticated user instead of a route parameter (e.g. a UUID)

Categories: Drupal

Dcycle: Start unit testing your Drupal and other PHP code today

Planet Drupal - 15 October 2019 - 5:00pm

Unit tests are the fastest, most reliable kinds of tests: they confirm that the smallest units of your code, i.e. class methods, work as expected.

Unit tests do not require a full environment with a database and external libraries; this makes unit tests extremely fast.

In this article we will look at how to take any PHP code – a Drupal site or module, or indeed any other PHP codebase unrelated to Drupal – and start unit testing it today. We’ll start by setting up tests which work for any PHP code, and then we’ll see how to run your tests on the Drupal testbot if you so desire.

Before we start testing

Unit tests are useless unless they are run on every change (commit) to a codebase through continuous integration (CI). And it’s excruciatingly painful to make CI work without some sort of platform-agnostic DevOps setup (we’ll use a Docker-based workflow), so before we even start testing, we’ll set up CI and Docker.

Docker for all things

In the context of this article, we’ll define DevOps as a way to embed all dependencies within our code, meaning we want to limit the number of dependencies on our computer or CI server to run our code. To do this, we will start by installing and starting Docker Desktop.

Once you’ve set it up, confirm you have Docker running:

docker -v # Docker version 19.03.2, build 6a30dfc

At this point, we can be assured that any code we run through Docker will run on any machine which has Docker installed. In this article we’ll use mostly PHPUnit, so instead of installing and configuring PHPUnit on our computer and our CI server and our colleagues’ computers, we can simply make sure our computer and our CI server have Docker installed, and run:

docker run --rm phpunit/phpunit --version

The first time this is run on an environment, it should result in:

Unable to find image 'phpunit/phpunit:latest' locally latest: Pulling from phpunit/phpunit Digest: sha256:bbbb143951f55fe93dbfed9adf130cae8623a1948f5a458e1aabbd175f7cb0b6 Status: Downloaded newer image for phpunit/phpunit:latest PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

On subsequent runs it will result in:

PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

Installing PHPUnit can also be done through Composer. In this article we won’t use Composer because

  • that would require us to manage a specific version of PHP on each machine;
  • Composer does not work for programming languages other than PHP (say, for example, we want to unit test Javascript or Python).

Let’s get started!

Host your code on Github or Bitbucket

We will avoid getting ahead of ourselves by learning and using Drupal’s unit test classes (which are based on PHPUnit) and testing infrastructure (we’ll do that below): we want to start by understanding how to unit test any PHP code (Drupal or otherwise).

To that end, we will need to host our code (or a mirror thereof) on non-Drupal infrastructure. Github and Bitbucket both integrate with CircleCI, a free, fast, and easy cloud continuous integration (CI) service with no vendor lock-in; we’ll use CircleCI later on in this article. With understanding of general unit testing principles under your belt, you can later move on to use framework-specific (including Drupal-specific) testing environments if you deem it necessary (for example if you are a contributor to core or to contrib modules which follow Drupal’s testing guidelines).

To demonstrate the principles in this article, I have taken a random Drupal 8 module which, at the time of this writing, has no unit tests, Automatic Entity Label. My selection is completely arbitrary, and I don’t use this module myself, and I’m not advocating you use it or not use it.

So, as my first step, I have added v. 8.x-3.0-beta1 of this module as is to Github, and tagged it as “original”.

You can see the version I uploaded to Github, without tests, here. There are no unit tests – yet.

Start continuous integration

Because, as we mentioned above, automated testing is all but useless without continuous integration (CI) to confirm your tests are passing, the next step is to set up CI. Attaching CircleCI to Github repos is straightforward. I started by adding a test that simply confirms that we can access PHPUnit on our CI environment.

Here is the changes I made to my code to add continuous integration. At this stage, this code only confirms that PHPUnit can be run via Docker, nothing else. If you want to follow along with your own codebase, you can add the same minor changes (in fact you are encouraged to do so). The change to the README.md document is a “Badge” which displays as green if tests pass, and red if they don’t, on the project’s home page. The rest is straightforward.

Once your code is set up for CI integration, create an account and log on to CircleCI using your Github account (Bitbucket works also), select your project from your list of projects (“Set Up Project” button), and start building it (“Start Building” button); that’s it!

Here is my very first build for my version of Auto Entity Label. It is worth unfolding the “Tests” section and looking at the test results:

./scripts/ci.sh Unable to find image 'phpunit/phpunit:latest' locally latest: Pulling from phpunit/phpunit Digest: sha256:bbbb143951f55fe93dbfed9adf130cae8623a1948f5a458e1aabbd175f7cb0b6 Status: Downloaded newer image for phpunit/phpunit:latest PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

You’ll notice that you have output very similar to what you have on your own computer. That’s the magic of Docker: build once, run anywhere. Without it, Continuous Integration is like pulling teeth.

Setting up PHPUnit to actually run tests

Before we can test anything, PHPUnit needs to know where the tests reside, which tests to run, and how to autoload classes based on their namespace. Different frameworks, including Drupal, have recommendations on all this, but to get a good idea of how PHPUnit works, let’s start from scratch by creating four new files in our project (keep them empty for now):

  • ./phpunit.xml, at the root of our project, will define where are tests are located, and where our autoloader is located.
  • ./phpunit-autoload.php, at the root of our project, is our autoloader; it tells PHPUnit that, for example, the namespace Drupal\auto_entitylabel\AutoEntityLabelManager corresponds to the file src/AutoEntityLabelManager.
  • ./phpunit-bootstrap.php, we’ll leave empty for now, and look at it later on.
  • ./tests/AutoEntityLabelManagerTest.php, which will contain a test for the AutoEntityLabelManager class.
phpunit.xml

In this file, we’ll tell PHPUnit where to find our tests, and where the autoloader is. Different developers have their own preferences for what to put here, and Drupal has specific recommendations, but for now we’ll just use a simple file declaring that our tests are in ./tests (although they could be anywhere), and that the file phpunit-autoload.php (you could name it anything) should be loaded before each test is run:

<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="phpunit-autoload.php"> <testsuites> <testsuite name="myproject"> <directory>./tests</directory> </testsuite> </testsuites> </phpunit> phpunit-autoload.php

In this file, we’ll tell PHPUnit how to find files based on namespaces. Different projects do this differently. For example, Drupal 7 has a custom Drupal-only way of autoloading classes; Drupal 8 uses the PSR-4 standard. In our example, we’re telling PHPUnit that any code which uses the class Drupal\auto_entitylabel\Something will load the corresponding file ./src/Something.php:

<?php /** * @file * PHPUnit class autoloader. * * PHPUnit knows nothing about Drupal, so provide PHPUnit with the bare * minimum it needs to know in order to find classes by namespace. * * Used by the PHPUnit test runner and referenced in ./phpunit.xml. */ spl_autoload_register(function ($class) { if (substr($class, 0, strlen('Drupal\\auto_entitylabel\\')) == 'Drupal\\auto_entitylabel\\') { $class2 = str_replace('Drupal\\auto_entitylabel\\', '', $class); $path = 'src/' . str_replace('\\', '/', $class2) . '.php'; require_once $path; } }); phpunit-bootstrap.php

(We’ll leave that one empty for now, but later on we’ll use it to put dummy versions of classes that Drupal code expects to find.)

tests/AutoEntityLabelManagerTest.php

Here is our first test. Let’s start with a very simple unit test: once which tests a pure function with no externalities.

Let’s take AutoEntityLabelManager::auto_entitylabel_entity_label_visible().

Here it is context, and here is the actual code we want to test:

public static function auto_entitylabel_entity_label_visible($entity_type) { // @codingStandardsIgnoreEnd $hidden = [ 'profile2' => TRUE, ]; return empty($hidden[$entity_type]); }

This is actual code which exists in the Auto Entity Label project; I have never tried this function in a running Drupal instance, I’m not even sure why it’s there, but I can still test it. I assume that if I call AutoEntityLabelManager::auto_entitylabel_entity_label_visible('whatever'), I should get TRUE as a response. This is what I will test for in ./tests/AutoEntityLabelManagerTest.php:

<?php namespace Drupal\auto_entitylabel\Tests; use Drupal\auto_entitylabel\AutoEntityLabelManager; use PHPUnit\Framework\TestCase; /** * Test AutoEntityLabelManager. * * @group myproject */ class AutoEntityLabelManagerTest extends TestCase { /** * Test for auto_entitylabel_entity_label_visible(). * * @cover ::auto_entitylabel_entity_label_visible */ public function testAuto_entitylabel_entity_label_visible() { $this->assertTrue(AutoEntityLabelManager::auto_entitylabel_entity_label_visible('whatever') === TRUE, 'Label "whatever" is visible.'); } }

For test methods to be called by PHPUnit, they need to start with a lowercase test.

(If you have looked at other Drupal unit testing tutorials, you might have noticed that Drupal unit tests are based not on PHPUnit\Framework\TestCase but on Drupal\Tests\UnitTestCase. The latter provides some useful, but not critical, helper code. In our case, using PHPUnit directly without Drupal means we don’t depend on Drupal to run our code; and we can better understand the intricacies of PHPUnit.)

scripts/ci.sh

Finally we’ll need to tweak ./scripts/ci.sh a bit:

docker run --rm -v "$(pwd)":/app phpunit/phpunit \ --group myproject

Adding -v "$(pwd)":/app shares our code on our host computer or server with a directory called /app on the PHPUnit Docker container, so PHPUnit actually has access to our code. --group myproject runs all tests in the “myproject” group (recall that in tests/AutoEntityLabelManagerTest.php, we have added @group myproject to the class comment).

Here are the changes we made to our code.

Running our first test… and running into our first problem

With all those changes in place, if you run ./scripts/ci.sh, you should have this output:

$ ./scripts/ci.sh PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors.

…and this Fatal error…

PHP Fatal error: Trait 'Drupal\Core\StringTranslation\StringTranslationTrait' not found in /app/src/AutoEntityLabelManager.php on line 16 ...

So what’s happening here? It turns out AutoEntityLabelManager uses something called StringTranslationTrait. A PHP trait is a code sharing pattern. It’s a fascinating topic and super useful to write testable code (we’ll get to it later); but right now we don’t need it and don’t really care about it, it’s just getting in the way of our test. We somehow need to tell PHPUnit that Drupal\Core\StringTranslation\StringTranslationTrait needs to exist, but we don’t really care – right now – what it does.

That’s where our phpunit-bootstrap.php file comes in. In it, we can define Drupal\Core\StringTranslation\StringTranslationTrait so that PHP will not complain that it does not exit.

In phpunit-autoload.php, require phpunit-bootsrap.php:

require_once 'phpunit-bootstrap.php';

And in phpunit-bootsrap.php, define a dummy version of Drupal\Core\StringTranslation\StringTranslationTrait:

<?php /** * @file * * PHPUnit knows nothing about Drupal. Declare required classes here. */ namespace Drupal\Core\StringTranslation { trait StringTranslationTrait {} }

Here is the diff in our repo.

Running our first passing test!

This is a big day for you, it’s the day of your first passing test:

$ ./scripts/ci.sh PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors. . 1 / 1 (100%) Time: 124 ms, Memory: 4.00MB OK (1 test, 1 assertion)

Because of the magic of Docker, the same output can be found on our CI infrastructure’s equivalent passing test (by unfolding the “Tests” section) once we push our code to Github.

Introducing test providers

OK, we’re getting into the jargon of PHPUnit now. To introduce the concept of test providers, consider this: almost every time we run a test, we’d like to bombard our unit (our PHP method) with a variety of inputs and expected outputs, and confirm our unit always works as expected.

The basic testing code is always the same, but the inputs and expected outputs change.

Consider our existing test:

/** * Test for auto_entitylabel_entity_label_visible(). * * @cover ::auto_entitylabel_entity_label_visible */ public function testAuto_entitylabel_entity_label_visible() { $this->assertTrue(AutoEntityLabelManager::auto_entitylabel_entity_label_visible('whatever') === TRUE, 'Label "whatever" is visible.'); }

Maybe calling our method with “whatever” should yield TRUE, but we might also want to test other inputs to make sure we cover every possible usecase for the method. In our case, looking at the method, we can reasonably surmise that calling it with “profile2” should yield FALSE. Again, I’m not sure why this is; in the context of this tutorial, all I want to do is to make sure the method works as expected.

So the answer here is to serarate the testing code from the inputs and expected outputs. That’s where the provider comes in. We will add arguments to the test code, and define a separate function which calls our test code with different arguments. The end results looks like this (I also like to print_r() the expected and actual output in case they differ, but this is not required):

/** * Test for auto_entitylabel_entity_label_visible(). * * @param string $message * The test message. * @param string $input * Input string. * @param bool $expected * Expected output. * * @cover ::auto_entitylabel_entity_label_visible * @dataProvider providerAuto_entitylabel_entity_label_visible */ public function testAuto_entitylabel_entity_label_visible(string $message, string $input, bool $expected) { $output = AutoEntityLabelManager::auto_entitylabel_entity_label_visible($input); if ($output != $expected) { print_r([ 'output' => $output, 'expected' => $expected, ]); } $this->assertTrue($output === $expected, $message); } /** * Provider for testAuto_entitylabel_entity_label_visible(). */ public function providerAuto_entitylabel_entity_label_visible() { return [ [ 'message' => 'Label "whatever" is visible', 'input' => 'whatever', 'expected' => TRUE, ], [ 'message' => 'Label "profile2" is invisible', 'input' => 'profile2', 'expected' => FALSE, ], [ 'message' => 'Empty label is visible', 'input' => '', 'expected' => TRUE, ], ]; }

Here is the diff in GitHub.

At this point, we have one test method being called with three different sets of data, so the same test method is being run three times; running the test now shows three dots:

$ ./scripts/ci.sh PHPUnit 6.5.13 by Sebastian Bergmann, Julien Breux (Docker) and contributors. ... 3 / 3 (100%) Time: 232 ms, Memory: 4.00MB OK (3 tests, 3 assertions) Breaking down monster functions

It must be human nature, but over time, during development, functions tend to get longer and longer, and more and more complex. Functions longer than a few lines tend to be hard to test, because of the sheer number of possible execution paths, especially if there are several levels of control statements.

Let’s take, as an example, auto_entitylabel_prepare_entityform(). With its multiple switch and if statements, it has a cyclomatic complexity of 7, the highest in this codebase, according to the static analysis tool Pdepend. If you’re curious about finding your cyclomatic complexity, you can use the magic of Docker, run the following, and take a look at ./php_code_quality/pdepend_output.xml:

mkdir -p php_code_quality && docker run -it --rm -v "$PWD":/app -w /app adamculp/php-code-quality:latest php /usr/local/lib/php-code-quality/vendor/bin/pdepend --suffix='php,module' --summary-xml='./php_code_quality/pdepend_output.xml' .

See adamculp/php-code-quality for more details. But I digress…

Testing this completely would require close to 2 to the power 7 test providers, so the easiest is to break it down into smaller functions with a lower cyclomatic complexity (that is, fewer control statements). We’ll get to that in a minute, but first…

Procedural code is not testable, use class methods

For all but pure functions, procedural code like our auto_entitylabel_prepare_entityform(), as well as private and static methods, are untestable with mock objects (which we’ll get those later). Therefore, any code you’d like to test should exist within a class. For our purposes, we’ll put auto_entitylabel_prepare_entityform() within a Singleton class, like this, and name it prepareEntityForm(). (You don’t need to use a Singleton; you can use a Drupal service or whatever you want, as long as everything you want to test is a non-static class method.)

Our second test

So we put our procedural code in a class. But the problem remains: it’s too complex to fully cover with unit tests, so as a next step I recommend surgically removing only those parts of the method we want to test, and putting them in a separate method. Let’s focus on these lines of code, which can lead to this change in our code.

Object and method mocking, and stubs

Let’s consider a scenario where we want to add some tests to EntityLabelNotNullConstraintValidator::validate().

Let’s start by splitting the validate method into smaller parts, like this. We will now focus on testing a more manageable method with a lower cyclomatic complexity:

/** * Manage typed data if it is valid. * * @return bool * FALSE if the parent class validation should be called. */ public function manageTypedData() : bool { $typed_data = $this->getTypedData(); if ($typed_data instanceof FieldItemList && $typed_data->isEmpty()) { return $this->manageValidTypedData($typed_data); } return FALSE; }

Recall that in unit testing, we are only testing single units of code. In this case, the unit of code we are testing is manageTypedData(), above.

In order to test `manageTypedData() and nothing else, conceptually, we need to assume that getTypedData() and manageValidTypedData() are doing their jobs, we will not call them, but replace them with stub methods within a mock object.

We want to avoid calling getTypedData() and manageValidTypedData() because that would interfere with our testing of manageTypedData() – we need to mock getTypedData() and manageValidTypedData().

When we test manageTypedData() in this way, we need to replace the real getTypedData() and manageValidTypedData() with mock methods and make them return whatever we want.

PHPUnit achieves this by making a copy of our EntityLabelNotNullConstraintValidator class, where getTypedData() and manageValidTypedData() are replaced with our own methods which return what we want. So in the context of our test, we do not instantiate EntityLabelNotNullConstraintValidator, but rather, a mock version of that class in which we replace certain methods. Here is how to instantiate that class:

$object = $this->getMockBuilder(EntityLabelNotNullConstraintValidator::class) ->setMethods([ 'getTypedData', 'manageValidTypedData', ]) ->disableOriginalConstructor() ->getMock(); // We don't care how getTypedData() figures out what to return to // manageTypedData, but we do want to see how our function will react // to a variety of possibilities. $object->method('getTypedData') ->willReturn($input); // We will assume manageValidTypedData() is doing its job; that's not // what were are testing here. For our test, it will always return TRUE. $object->method('manageValidTypedData') ->willReturn(TRUE);

In the above example, our new object behaves exactly as EntityLabelNotNullConstraintValidator, except that getTypedData() returns $input (which we’ll define in a provider); and manageValidTypedData() always returns TRUE.

Keep in mind that private methods cannot be mocked, so for that reason I generally avoid using them; use protected methods instead.

Here is our initial test for this.

Our provider, at this point, only makes sure that if getTypedData() returns a new \stdClass() which is not an instanceof FieldItemList, then the method we’re testing will return FALSE.

Here is how we could extend our provider to make sure our method reacts correctly if getTypedData() returns a FieldItemList whose isEmpty() method returns TRUE, and FALSE.

Testing protecting methods

Let’s say we want to (partially) test the protected AutoEntityLabelManager::getConfig(), we need to introduce a new trick.

Start by taking a look at our test code which fails. If you try to run this, you will get:

There was 1 error: 1) Drupal\auto_entitylabel\Tests\AutoEntityLabelManagerTest::testGetConfig Error: Cannot access protected property Mock_AutoEntityLabelManager_0f5704cf::$config

So we want to test a protected method (getConfig()), and, in order to test it, we need to modify a protected property ($config). These two will result in “Cannot access”-type failures.

The solution is to use a trick known as class reflection; it’s a bit opaque, but it does allow us to access protected properties and methods.

Take a look at some changes which result in a working version of our test.

Copy-pasting is perhaps your best fiend here, because this concept kind of plays with your mind. But basically, a ReflectionClass allows us to retrieve properties and methods as objects, then set their visibility using methods of those objects, then set their values or call them using their own methods… As I said, copy-pasting is good, sometimes.

A note about testing abstract classes

There are no abstract classes in Auto Entity Label, but if you want to test an abstract class, here is how to create a mock object:

$object = $this->getMockBuilder(MyAbstractClass::class) ->setMethods(NULL) ->disableOriginalConstructor() ->getMockForAbstractClass(); Using traits

Consider the following scenario: a bunch of your code uses the legacy drupal_set_message() method. You might have something like:

class a extends some_class { public function a() { ... drupal_set_message('hello'); ... } } class b extends some_other_class { public function b() { ... drupal_set_message('world'); ... } }

Your tests will complain if you try to call, or mock drupal_set_message() when unit-testing a::a() or b::b(), because drupal_set_message()` is procedural and you can’t do much with it (thankfully there is fewer and fewer procedural code in Drupal modules, but you’ll still find a lot of it).

So in order to make drupal_set_message() mockable, you might want to something like:

class a extends some_class { protected method drupalSetMessage($x) { drupal_set_message($x); } public function a() { ... $this->drupalSetMessage('hello'); ... } } class b extends some_other_class { protected method drupalSetMessage($x) { drupal_set_message($x); } public function b() { ... $this->drupalSetMessage('world'); ... } }

Now, however, we’re in code duplication territory, which is not cool (well, not much of what we’re doing is cool, not in the traditional sense anyway). We can’t define a base class which has drupalSetMessage() as a method because PHP doesn’t (and probably shouldn’t) support multiple inheritance. That’s where traits come in, it’s a technique for code reuse which is exactly adapted to this situation:

trait commonMethodsTrait { protected method drupalSetMessage($x) { drupal_set_message($x); } } class a extends some_class { use commonMethodsTrait; public function a() { ... $this->drupalSetMessage('hello'); ... } } class b extends some_other_class { use commonMethodsTrait; public function b() { ... $this->drupalSetMessage('world'); ... } }

Drupal uses this a lot: the t() method is peppered in most of core and contrib; earlier in this article we ran into StringTranslationTrait; that allows developers to use $this->t() instead of the legacy t(), therefore making it mockable when testing methods which use it. The great thing about this approach is that we do not even need Drupal’s StringTranslationTrait when running our tests, we can mock t() even if a dummy version of StringTranslationTrait is used.

Check out this test for an example.

What about Javascript, Python and other languages?

PHP has PHPUnit; other languages also have their test suites, and they, too, can run within Docker. Javascript has AVA; Python has unittest.

All unit test frameworks support mocking.

Let’s look a bit more closely at AVA, but we do not want to install and maintain it on all our developers’ machines, and on our CI server, so we’ll use a Dockerized version of AVA. We can download that project and, specifically, run tests against example 3:

git clone git@github.com:dcycle/docker-ava.git docker run -v $(pwd)/example03/test:/app/code \ -v $(pwd)/example03/code:/mycode dcycle/ava

The result here, again due to the magic of Docker, should be:

1 passed

So what’s going on here? We have some sample Javascript code which has a function we’d like to test:

module.exports = { dangerlevel: function(){ return this.tsunamidangerlevel() * 4 + this.volcanodangerlevel() * 10; }, tsunamidangerlevel: function(num){ // Call some external API. return this_will_fail_during_testing(); // During tests, we want to ignore this function. }, volcanodangerlevel: function(num){ // Call some external API. return this_will_fail_during_testing(); // During tests, we want to ignore this function. } }

In this specific case we’d like to mock tsunamidangerlevel() and volcanodangerlevel() during unit testing: we don’t care that this_will_fail_during_testing() is unknown to our test code. Our test could look something like this:

import test from 'ava' import sinon from 'sinon' var my = require('/mycode/dangerlevel.js'); test('Danger level is correct', t => { sinon.stub(my, 'tsunamidangerlevel').returns(1); sinon.stub(my, 'volcanodangerlevel').returns(2); t.true(my.dangerlevel() == 24); })

What we’re saying here is that if tsunamidangerlevel() returns 1 and volcanodangerlevel() returns 2, then dangerlevel() should return 24.

The Drupal testbot

Drupal has its own Continuous Integration infrastructure, or testbot. It’s a bit more involving to reproduce its results locally; still, you might want to use if you are developing a Drupal module; and indeed you’ll have to use if it you are submitting patches to core.

In fact, it is possible to tweak our code a bit to allow it to run on the Drupal testbot and CircleCI.

Here are some changes to our code which allow exactly that. Let’s go over the changes required:

  • Tests need to be in ./tests/src/Unit;
  • The @group name should be unique to your project (you can use your project’s machine name);
  • The tests should have the namespace Drupal\Tests\my_project_machine_name\Unit or Drupal\Tests\my_project_machine_name\Unit\Sub\Folder (for example Drupal\Tests\my_project_machine_name\Unit\Plugin\Validation);
  • The unit tests have access to Drupal code. This is actually quite annoying, for example, we can no longer just create an anonymous class for FieldItemList but rather, we need to create a mock object using disableOriginalConstructor(); this is because, the unit test code being aware of Drupal, it knows that FieldItemList requires parameters to its constructor; and therefore it complains when we don’t have any (in the case of an anonymous object).

To make sure this works, I created a project (it has to be a full project, as far as I can tell, can’t be a sandbox project, or at least I didn’t figure out to do this with a sandbox project) at Unit Test Tutorial. I then activated automated testing under the Automated testing tab.

The results can be seen on the Drupal testbot. Look for these lines specifically:

20:32:38 Drupal\Tests\auto_entitylabel\Unit\AutoEntityLabelSingletonT 2 passes 20:32:38 Drupal\Tests\auto_entitylabel\Unit\AutoEntityLabelManagerTes 4 passes 20:32:38 Drupal\Tests\auto_entitylabel\Unit\Plugin\Validation\EntityL 1 passes 20:32:38 Drupal\Tests\auto_entitylabel\Unit\Form\AutoEntityLabelFormT 1 passes

My main annoyance with using the Drupal testbot is that it’s hard to test locally; you need to have access to a Drupal instance with PHPUnit installed as a dev dependency, and a database. To remedy this, the Drupal Tester Docker project can be used to run Drupal-like tests locally, here is how:

git clone https://github.com/dcycle/drupal-tester.git cd drupal-tester/ mkdir -p modules cd modules git clone --branch 8.x-1.x https://git.drupalcode.org/project/unit_test_tutorial.git cd .. ./scripts/test.sh "--verbose --suppress-deprecations unit_test_tutorial" docker-compose down -v

This will give you more or less the same results as the Drupal testbot:

Drupal\Tests\auto_entitylabel\Unit\AutoEntityLabelManagerTes 4 passes Drupal\Tests\auto_entitylabel\Unit\AutoEntityLabelSingletonT 2 passes Drupal\Tests\auto_entitylabel\Unit\Form\AutoEntityLabelFormT 1 passes Drupal\Tests\auto_entitylabel\Unit\Plugin\Validation\EntityL 1 passes In conclusion

Our promise, from the title of this article, is “Start unit testing your PHP code today”. Hopefully the tricks herein will allow you to do just that. My advice to you, dear testers, is to start by using Docker locally, then to make sure you have Continuous Integration set up (on Drupal testbot or CircleCI, or, as in our example, both), and only then start testing.

Happy coding!

Unit tests are the fastest, most reliable kinds of tests: they confirm that the smallest units of your code, i.e. class methods, work as expected.

Categories: Drupal

Paragraphs jQuery UI Tabs

New Drupal Modules - 15 October 2019 - 4:08pm

Paragraphs jQuery UI Tabs is a module to create paragraphs with tabs effect in your content.
It based on jQuery UI Tabs plugin which already included in core, so no need to install additional libraries.
See examples on https://jqueryui.com/tabs/

Installation:

Categories: Drupal

Zencoder Transcoding

New Drupal Modules - 15 October 2019 - 3:30pm

Zencoder transcoder integration for the Transcoding API module.

Categories: Drupal

Licenses Vocabulary

New Drupal Modules - 15 October 2019 - 2:22pm

This module takes the inspiration from https://www.drupal.org/project/media_attribution but is not limited to media.
It just creates the taxonomy so you are free to attach the field to any content or entity.

It also imports a default set of licenses from the file licenses_vocabulary.default.licenses.yml. It will import basic creative commons 4.0 and CC0 licenses.
Additionally it can import licenses from textarea in settings.

Categories: Drupal

RoadRunner

New Drupal Modules - 15 October 2019 - 12:49pm
Categories: Drupal

ComputerMinds.co.uk: A decade of Drupal

Planet Drupal - 15 October 2019 - 6:27am

Last month I begun my second decade of working with Drupal! How crazy is that? I started at ComputerMinds in 2009. Drupalcon Paris was my first week on the job - I just remember taking so many notes, as if it were a university course! I had a lot to learn then, but now I can look back with a much more experienced head, hopefully wiser, with some sort of perspective.

The conference room before the keynote at Drupalcon Paris 2009, when my career with Drupal began.

 

My first steps with Drupal were on Drupal 6. It could do a lot (at least once CCK and Views were installed), and Wordpress was much smaller so the two were still occupying a similar space in the market. Most of the sites we built were trying to be a mini social network, a blog, or a brochure site, sometimes with e-commerce thrown in. There were rounded corners everywhere, garish pinks, and some terribly brittle javascript. Supporting Internet Explorer 6 was a nightmare, but still necessary.

It's probably only over the last few years that it became clear that Drupal's strength is its ability to be turned to just about anything that a website needs to do. That has meant that whilst alternative products have picked up the simpler sites, Drupal has been brilliant for projects with complex requirements. Integrating with CRMs and all sorts of other APIs, handling enormous traffic loads, providing content for apps - this kind of stuff has always been Drupal's jam. You just have to know how to get it hooked up in all the right places!

Speaking of hooks, it's been interesting to see Drupal move from its famous magical hooks, towards event-driven architecture. For me, that single shift represented an enormous change in direction for Drupal. I believe the events/subscriber pattern, as a part of a wider effort in Drupal 8 to deliberately use existing well-defined design patterns and solutions, is a sign of a much more mature and professional platform. Most coding problems have been solved well elsewhere, we shouldn't reinvent the wheel! (Although I know I can be guilty of that!) That's just one example of how the Drupal ecosystem has become more professional over the last ten years. Formal testing is another example. Many people have felt left behind by this shift, as a need in the enterprise world that Drupal could meet was identified. 'Enterprise' gets used as a dirty word sometimes - but to be frank, there's more money and more suitable opportunity there!

That is something the Drupal community has to be honest about. It is rightly aiming to champion diversity, and be as accessible as possible for as many as possible across the world (I especially love that Drupal is now so good for multilingual projects). It's not like Drupal is suddenly inappropriate for smaller projects - in fact, I'd still suggest it's far ahead in many aspects.

But money makes things happen, and gives people their livelihood. I appreciate seeing honesty and innovation about this coming from community leaders. Thankfully those kinds of values are what drive the Drupal project, even if money is often the facilitator. As a community we must always fight to keep those things in the right relation to each other: money has an inevitable influence that we must accept, but it must be led by us and our values, not the other way around. I should add that I am very aware that I am privileged to be a developer in a leading Drupal agency, so my opinion will be shaped by that position!

To end with honesty and innovation myself, I would love to see the following carried into the Drupal project's next 10 years, which I saw the community espouse over the last decade. I know I need to grow in these things myself!

  • Maintain excellence, even as the make-up of the community changes.
  • Deliberately listen to under-represented portions of the community, as an intentional force against the skewing effect of money/power.
  • Keep watch for what competitors and other relevant products are doing, to incorporate worthwhile things wherever possible.
  • Reflect on the strengths & weaknesses of Drupal (and its community) with honesty. Let's make the most of what makes Drupal what it is and not be afraid for it to focus on that; the same goes for us as individuals.

I'm proud to work with Drupal, long may it continue!

Categories: Drupal

JSON:API Reference

New Drupal Modules - 15 October 2019 - 5:47am

JSON:API reference provides a field type Typed Resource Object, that is similar to an entity reference field. However, rather than refer to an entity on the same Drupal installation, this refers to a resource object exposed via JSON:API.

For example - if you are syndicating content from one system to another, you may want to create a typed resource object field on the user entity type on the source system, which refers to the corresponding user on the destination system.

Categories: Drupal

IMCE Private Public Buttons

New Drupal Modules - 15 October 2019 - 4:42am

The IMCE Private module adds seperate IMCE buttons for private and public files to CKEditor, so you can access the non-default filesystem.

Categories: Drupal

Taxonomy export csv

New Drupal Modules - 15 October 2019 - 4:11am

This module provides a taxonomy terms export in CSV format for Drupal taxonomy terms 7 to 8 migration.

INSTALLATION:
-----------------
1. Goto your Drupal site, path: sites/all/modules.

2. Enable the "Taxonomy export csv" module by navigating to:

administer > modules

CONFIGURATION:
-------------------
1. Go to below URL:
admin/config/system/migrate_taxonomy

2. Select vocabulary and click "Export terms" button.

RESULT:
----------
Download expected vocabulary terms in CSV format.

Categories: Drupal

Lang switch as per browser lang

New Drupal Modules - 15 October 2019 - 3:02am

Show a modal popup to choose languages enabled in Drupal as per browser default language.

Categories: Drupal

Specbee: How to create a custom Layout builder in Drupal 8

Planet Drupal - 15 October 2019 - 2:58am
How to create a custom Layout builder in Drupal 8 Neslee Canil Pinto 15 Oct, 2019 Top 10 best practices for designing a perfect UX for your mobile app

Drupal 8 is making things so much easier for content authors. One of the most important reasons being the addition of the Layout Builder module in core Drupal 8. With easy drag and drop interface, preview and customizing abilities, the layout builder is soon becoming a favorite page-building and designing tool.

In my previous article, I wrote about how you can get started with installing and using Drupal 8’s Layout builder. Here, I want to share my knowledge on customizing a layout builder for unique requirements.

If your website needs multiple sections with multiple blocks, then you can’t use the default sections that are provided by Drupal. For this, you can create your own custom layout.

Getting Started

We will first create a custom module for our custom layout builder We will name the folder as custom_layout.Next, we will create an info.yml file. We should specify the basic keys for it. Custom layout builder will have a dependency on the layout builder module. Let us specify it over here.

Code: name: 'Custom Layout' type: module description: 'Provides a way for building layout' core: 8.x package: 'Custom' dependencies: - layout_builder:layout_builder

Next we will create layouts.yml file to specify regions for our custom layout.

  • custom_layout: key for our custom layout.
  • label: Label for our custom layout.
  • category: Category for our custom layout.
  • default_region:  Default region are the regions which are default in all type of layout.
  • icon_map: Icon which will be shown up while we choose our layout.

Eg:
 

To create above icon map we need to follow below steps.

1. First row is “Header Left” and “Header Right”
         We have specified - [header_left, header_left, header_right] - header_left is             
         defined 2 times so it will take 2 parts of total width of the   screen then header_right will take rest of the portion so ratio will be  “75%/25%”.
2. Second row is “Content” and “Sidebar”
        We have specified - [content, content, sidebar]   same above logic applied here.    
3.  Third row is “Footer Left” and “Footer Right”
        We have specified - [footer_left, footer_right] -  since there are only 2 regions it will take 50% each.
        

  • regions: Regions which we need for our layout. We have header_left, header_right, sidebar, content, footer_left, footer_right.

Code:

custom_layout: label: 'Custom Layout' category: 'Custom Layouts' default_region: content icon_map: - [header_left, header_left, header_right] - [content, content, sidebar] - [content, content, sidebar] - [content, content, sidebar] - [footer_left, footer_right] regions: header_left: label: Header Left header_right: label: Header Right sidebar: label: Sidebar content: label: Content footer_left: label: Footer Left footer_right: label: Footer Right

Next, let us create an html structure for our layout. We will create a folder named “layouts” within our module. In the folder we will create another folder named “custom_layout”

And within that folder, we will create twig file named “custom-layout.html.twig” 


We have to specify the twig file in layouts.yml 

  • path: It specifies in which folder your html structure will be written
  • template: It specifies which twig template to use for this layout under the path.

 

Code: custom_layout: label: 'Custom Layout' path: layouts/custom_layout category: 'Custom Layouts' template: custom-layout default_region: content icon_map: - [header_left, header_left, header_right] - [content, content, sidebar] - [content, content, sidebar] - [content, content, sidebar] - [footer_left, footer_right] regions: header_left: label: Header Left header_right: label: Header Right sidebar: label: Sidebar content: label: Content footer_left: label: Footer Left footer_right: label: Footer Right

Next we will write html structure for our regions in “custom-layout.html.twig” file.
We will set classes has “layout” and “layout--custom-layout”  and we will wrap the whole content inside it.
We will specify regions which where been defined in layouts.yml , we can access those regions like “{{ content.header_left }}”
 

Code: 

{% set classes = [
 'layout',
 'layout--custom-layout',
] %}
{% if content %}
 
   {% if content.header_left %}
     
       {{ content.header_left }}
     
   {% endif %}
   {% if content.header_right %}
     
       {{ content.header_right }}
     
   {% endif %}
   {% if content.content %}
     
       {{ content.content }}
     
   {% endif %}
   {% if content.sidebar %}
     
       {{ content.sidebar }}
     
   {% endif %}
   {% if content.footer_left %}
     
       {{ content.footer_left }}
     
   {% endif %}
   {% if content.footer_right %}
     
       {{ content.footer_right }}
     
   {% endif %}
 
{% endif %}

After the html structure is written, we will have to write css for each region. We will now create libraries.yml in our custom module.

 

Code:

custom_layout: version: VERSION css: theme: css/custom_layout.css: {}

We will define that library in layouts.yml

  

Code:

custom_layout: label: 'Custom Layout' path: layouts/custom_layout category: 'Custom Layouts' template: custom-layout library: custom_layout/custom_layout default_region: content icon_map: - [header_left, header_left, header_right] - [content, content, sidebar] - [content, content, sidebar] - [content, content, sidebar] - [footer_left, footer_right] regions: header_left: label: Header Left header_right: label: Header Right sidebar: label: Sidebar content: label: Content footer_left: label: Footer Left footer_right: label: Footer Right

Now let’s start with styling our regions block. We will specify structure  for each region as below-

Code:

.layout--custom-layout { display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-wrap: wrap; flex-wrap: wrap; } @media screen and (min-width: 40em) { .layout--custom-layout .layout__region--header_left { -webkit-box-flex: 0; -ms-flex: 0 1 70%; flex: 0 1 70%; } .layout--custom-layout .layout__region--header_right { -webkit-box-flex: 0; -ms-flex: 0 1 30%; flex: 0 1 30%; } .layout--custom-layout .layout__region--content { -webkit-box-flex: 0; -ms-flex: 0 1 70%; flex: 0 1 70%; } .layout--custom-layout .layout__region--sidebar { -webkit-box-flex: 0; -ms-flex: 0 1 30%; flex: 0 1 30%; } .layout--custom-layout .layout__region--footer_left { -webkit-box-flex: 0; -ms-flex: 0 1 50%; flex: 0 1 50%; } .layout--custom-layout .layout__region--footer_right { -webkit-box-flex: 0; -ms-flex: 0 1 50%; flex: 0 1 50%; } }

Next, let us enable our custom module
 

Let us go to Structure -> Content types and click on “Manage display” for any content type. For now we will use ‘article’ content type.

After we choose our custom layout-
 

 

I hope this brief tutorial helps in getting you started with creating a custom Layout Builder in Drupal 8.Our Drupal team can help you build high-performing and easy-to-use Drupal websites. Contact us today to know more.

 

Drupal Planet Shefali ShettyApr 05, 2017 Subscribe For Our Newsletter And Stay Updated Subscribe

Leave us a Comment

  Shefali ShettyApr 05, 2017 Recent Posts Image How to create a custom Layout builder in Drupal 8 Image Getting Started with Layout Builder in Drupal 8 - A Complete Guide Image Drupal Support and Maintenance –What you need to know and what should you expect? Explore Our Drupal Services TAKE ME THERE Featured Success Stories

Know more about our technology driven approach to recreate the content management workflow for [24]7.ai

link

Find out how we transformed the digital image of world’s largest healthcare provider, an attribute that defined their global presence in the medical world.

link

Develop an internal portal aimed at encouraging sellers at Flipkart to obtain latest insights with respect to a particular domain.

link
Categories: Drupal

Menu Title Length

New Drupal Modules - 15 October 2019 - 1:36am

This module allows to set the maximum length for Menu Title

Categories: Drupal

Third & Grove: It *IS* Possible — Achieving Flawless Performance with Our New Site

Planet Drupal - 15 October 2019 - 12:00am

In our recent rebrand at Third and Grove, we took on a Drupal headless build with a Gatsby front end. With any project where you are pushing the limits of what technologies are capable of, there were some growing pains. 

These growing pains resulted from a few other places too (developers less familiar with React and Gatsby, using new and actively changing tools). We ran into some issues that we thought were really strange with bundle sizes, which turned out to be due to the way we were querying images. We also ran into load time issues with some SVGs that were being handled with a library called svgr. We also had a few fonts to load. Well, 18 (yeah that’s right). As a last resort implementing our own lazy loading helped bring us in a perfect lighthouse score!

Categories: Drupal

Airtable

New Drupal Modules - 14 October 2019 - 11:58pm
INTRODUCTION

This modules provides a connection between Airtable and Drupal. By exposing external tables via config entities, the content of those tables can be used internally.

REQUIREMENTS
  • Key: we use the Key-module in order to store the
    Airtable API Key.
INSTALLATION

Install this module via Composer using `composer req drupal/airtable`.

Categories: Drupal

GraphQL APQ

New Drupal Modules - 14 October 2019 - 11:31pm

Drupal module for Automatic Persisted Queries compatible with the apollo-link-persisted-queries project's protocol.

Categories: Drupal

Promet Source: Site Gives Librarians Tools to Encourage Youth to Code

Planet Drupal - 14 October 2019 - 12:08pm
There’s no question that literacy in the 21st Century is a multi-faceted concept that extends far beyond books on the shelves.
Categories: Drupal

Commerce Paga+Tarde

New Drupal Modules - 14 October 2019 - 11:58am

The extension provides Paga + Tarde ( https://www.pagamastarde.com/ ) payment integration for Drupal Commerce 2 which enables payment processing using financing.

Benefits for your customers
Differentiate with competition via financing payments!

Paga + Tarde offers the way to finance your sales, allowing customers split the payments and boost the sales.

Features
- Provide partial payments
- Boost sales
- No technical configuration

Categories: Drupal

Pages

Subscribe to As If Productions aggregator - Drupal