They say static methods are hard to test. They are wrong. Static methods are easy to test, but hard to mock. Unfortunately developing software inside Drupal requires implementing several static method calls. Some of them may be avoided, like getting services, which may be easily injected as a dependencies (and that’s actually how it should be done).
Some would say, you can mock container and then set services’ mocks, but this is really wrong approach. This is not unit testing. Unit testing is about testing only the part of code you create, nothing more. This is because every unit test should have only one reason to fail and this reason should be mentioned in the test name. If your test relies on some additional code, it may fail, if someone makes changes to this additional code. You wouldn’t want to fix dozens of tests because of some little change, would you?
However in Drupal there are some static calls which may not be injected, like Drupal\user\Entity\User::load. One way to solve this problem is mock whole User class and require mock in test file. This will work if your mock will be using Drupal\user\Entity namespace, because if you require mock file in the top of your test file, autoloading mechanism won’t look for class implementation, because class will be already loaded. Unfortunately this code forces you to write too much code, like setters for users to mock, etc.
UserMock.php
|
|
Test class
|
|
There is actually better way to solve this problem: not using static calls in your class. To do so, you may create wrapper shared by objects like a service, which may be injected at will. But writting wrapper which delegates all it’s calls to a static class might be a pain, so it got me thinking and I found a nice solution to that – anonymous functions and proper constructor. So, there are some mandatory parameters which need to be passed when creating mocked object, but after them there are optional parameters with default values (or an array of them) which if set, will redefine default lambdas. And if one would like to mock a static call used in tested class, they will just pass proper anonymous function to created object. So the code will look like this:
Tested class
|
|
Unit test
|
|
And that’s it. You can safely use static calls in your code and easily mock them.