Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor filter_search_query method #5

Open
ankitrox opened this issue Sep 25, 2020 · 1 comment
Open

Refactor filter_search_query method #5

ankitrox opened this issue Sep 25, 2020 · 1 comment

Comments

@ankitrox
Copy link

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.

@SH4LIN
Copy link
Contributor

SH4LIN commented Dec 13, 2023

@ankitrox Thank you for the explanation.

The plan of action on this issue can be:

  1. Create a factory class called SearchEngineFactory inside that we will implement functionality to instantiate the SearchEngine class object.
  2. Create an object of this factory in the main class file i.e. class-plugin.php
  3. Inject/Pass the factory object wherever it is required in our case to the Search class.
  4. Define a factory class variable as suggested in the above example. And whenever required use this factory object to create the instance/fetch the object of SearchEngine class.

CC: @joelabreo227 @dhawalepranav

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants