-
Notifications
You must be signed in to change notification settings - Fork 2
Description
TLDR
What the heck does "Testing in isolation" mean?
When developing a plugin, the best way to test plugin is without loading the WordPress environment. If you write code that can be easily tested without WordPress, your code becomes better.
Every component that is unit tested, should be tested in isolation: when you test a class, you only have to test that specific class, assuming all other code is working perfectly.
So, testing in isolation means testing a component (class
in this case) at a time without loading any external dependencies. Those external dependencies can even be other classes in same plugin.
This is the reason why unit tests are called "unit". Also, without loading core, tests will run much faster which will be a huge benefit if we integrate unit tests in our CI/CD pipeline because deployment job will be completed much quicker
Example of problem
Consider there are two classes School
and Student
. Suppose, School
class have some method register
for admission of new student.
class School {
public $name;
public $address;
public function __construct( $name, $address ) {
$this->name = 'Fake school';
$this->address = 'Online nowadays.. lol';
}
public function register_student( $name, $age, $gender ) {
$student = new Student( $name, $age, $gender );
// Do something with student object below
}
}
Note: I'm not writing Student
class because it is not required for explaining the issue.
Now, suppose you want to test School
class in isolation (according to definition given above), register_student
method would not be feasible to test because your tests have to be aware of Student
class' structure in order to test register_student
method which contradicts the idea of testing in isolation.
Here, we can deal with this situation by passing Student
object to register_student
method directly as
public function register_student( Individual $student ) {
// Do something with student object below
}
Individual
is an interface because a school can have different individuals like Employees, Students, Vendors etc. and all will have some common properties and methods like name, gender, age etc.
Above method can be easily tested using Mock, we can pass Student
mock to register_student
method in order to test that method. In this case, our test class do not need to have knowledge of Student
class' structure in order to test School
class and thus we achieve Testing in isolation
🥳
Another solution
Other solution can be to use Factory pattern to get objects build. So we can create a separate factory class/method to get Student
object.
class IndidualFactory {
public function make( string $type ): Individual {
switch ( $type ) {
case 'student':
return new Student();
case 'employee':
return new Employee();
default:
throw new Exception( 'Type is inappropriate.' );
}
}
}
and our School
class can be something like
class School {
public $name;
public $address;
private $factory;
public function __construct( IndidualFactory $factory ) {
$this->factory = $factory;
}
public function register_student( $name, $age, $gender ) {
$student = $this->factory->make('student');
$student->name = $name;
$student->age = $age;
$student->gender = $gender;
// Do something with student object below
}
}
register_student
in above example can also be tested because we can mock the factory class while testing the School
class and can pass that mock to School
's constructor via dependency injection.
Actual issue in current code
filter_search_query
method in class-search.php
is instantiating class Search_Engine
inside its body, so it will be difficult to test in isolation as shown above.
Golden rule
Never instantiate any class inside method body. Either use Factories or use DI, so that code should be testable in unit
.