diff --git a/.gitignore b/.gitignore
index ccef0560f5e81eb78c47d43c0464ed4e04ee6434..5e55e37cdaf8d535a3ded8dabacb847b3daa74db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 /vendor/
-.idea
\ No newline at end of file
+.idea
+composer.lock
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 18c9f963211854a95ed55ebd922e47cc2f092f82..95f8e78db5678ea5d222a31e7efbeb708c73eb78 100644
--- a/composer.json
+++ b/composer.json
@@ -10,6 +10,7 @@
         "php": ">=5.2"
     },
     "require-dev": {
+        "php": ">=5.5",
         "phpunit/phpunit": "<7",
         "wp-coding-standards/wpcs": "^0.14.1",
         "squizlabs/php_codesniffer": "^3.0.2",
@@ -18,14 +19,14 @@
         "wimg/php-compatibility": "^8"
     },
     "autoload": {
+
     },
     "autoload-dev": {
+        "classmap": ["src", "tests"]
     },
     "scripts": {
         "phpcs": "phpcs",
         "phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
-        "phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
-        "phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
-        "phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
+        "phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage"
     }
 }
diff --git a/phpunit-unit.xml b/phpunit-unit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..72701bbecbc7b15b8b408ee193a74a0f131d5615
--- /dev/null
+++ b/phpunit-unit.xml
@@ -0,0 +1,21 @@
+<phpunit bootstrap="tests/unit/bootstrap.php">
+    <testsuites>
+        <testsuite>
+            <directory prefix="Test_" suffix=".php">./tests/unit/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">src</directory>
+        </whitelist>
+    </filter>
+
+    <logging>
+        <log type="junit" target="build-coverage/report.junit.xml"/>
+        <log type="coverage-html" target="build-coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
+        <log type="coverage-text" target="build-coverage/coverage.txt"/>
+        <log type="coverage-clover" target="build-coverage/clover.xml"/>
+    </logging>
+
+</phpunit>
\ No newline at end of file
diff --git a/src/Basic_Requirement_Checker.php b/src/Basic_Requirement_Checker.php
index 6de82bf3692c243d10aa2ba5bd44ee414e86f1ea..0a439d121b6cf2cae2f95a1411de6cb4c25b5cc7 100644
--- a/src/Basic_Requirement_Checker.php
+++ b/src/Basic_Requirement_Checker.php
@@ -1,6 +1,6 @@
 <?php
 
-if (!class_exists('WPDesk_Translable')) {
+if ( ! class_exists( 'WPDesk_Translable' ) ) {
 	require_once 'Translable.php';
 }
 
@@ -9,38 +9,32 @@ if (!class_exists('WPDesk_Translable')) {
  * have to be compatible with PHP 5.2.x
  */
 class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
+	const EXTENSION_NAME_OPENSSL = 'openssl';
+	const HOOK_ADMIN_NOTICES_ACTION = 'admin_notices';
+
 	/** @var string */
 	private $plugin_name = '';
-
 	/** @var string */
 	private $plugin_file = '';
-
 	/** @var string */
 	private $min_php_version;
-
 	/** @var string */
 	private $min_wp_version;
-
-	/** @var string */
-	private $min_wc_version;
-
+	/** @var string|null */
+	private $min_wc_version = null;
+	/** @var int|null */
+	private $min_openssl_version = null;
 	/** @var array */
 	private $plugin_require;
-
 	/** @var array */
 	private $module_require;
-
 	/** @var array */
 	private $setting_require;
-
 	/** @var array */
 	private $notices;
-
 	/** @var @string */
 	private $text_domain;
 
-	const EXTENSION_NAME_OPENSSL = 'openssl';
-
 	/**
 	 * @param string $plugin_file
 	 * @param string $plugin_name
@@ -62,10 +56,6 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		$this->notices         = array();
 	}
 
-	public function get_text_domain() {
-		return $this->text_domain;
-	}
-
 	/**
 	 * @param string $version
 	 *
@@ -99,6 +89,17 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		return $this;
 	}
 
+	/**
+	 * @param $version
+	 *
+	 * @return $this
+	 */
+	public function set_min_openssl_require( $version ) {
+		$this->min_openssl_version = $version;
+
+		return $this;
+	}
+
 	/**
 	 * @param string $plugin_name
 	 * @param string $nice_plugin_name Nice plugin name for better looks in notice
@@ -166,8 +167,15 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 				$this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wp_version ) );
 		}
 		if ( ! is_null( $this->min_wc_version ) && $this->can_check_plugin_version() && ! $this->is_wc_at_least( $this->min_wc_version ) ) {
-			$notices[] = $this->prepare_notice_message( sprintf( __( 'The &#8220;%s&#8221; plugin cannot run on WooCommerce versions older than %s. Please update WooCommerce.', $this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wc_version ) );
+			$notices[] = $this->prepare_notice_message( sprintf( __( 'The &#8220;%s&#8221; plugin cannot run on WooCommerce versions older than %s. Please update WooCommerce.',
+				$this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wc_version ) );
 		}
+		if ( ! is_null( $this->min_openssl_version ) && ! $this->is_open_ssl_at_least( $this->min_openssl_version ) ) {
+			$notices[] = $this->prepare_notice_message( sprintf( __( 'The &#8220;%s&#8221; plugin cannot run without OpenSSL module version at least %s. Please update OpenSSL module.',
+				$this->get_text_domain() ), esc_html( $this->plugin_name ),
+				'0x' . dechex( $this->min_openssl_version ) ) );
+		}
+
 		$notices = $this->append_plugin_require_notices( $notices );
 		$notices = $this->append_module_require_notices( $notices );
 		$notices = $this->append_settings_require_notices( $notices );
@@ -175,13 +183,46 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		return $notices;
 	}
 
+	/**
+	 * @param $min_version
+	 *
+	 * @return mixed
+	 */
+	public static function is_php_at_least( $min_version ) {
+		return version_compare( phpversion(), $min_version, '>=' );
+	}
+
+	/**
+	 * Prepares message in html format
+	 *
+	 * @param string $message
+	 *
+	 * @return string
+	 */
+	private function prepare_notice_message( $message ) {
+		return '<div class="error"><p>' . $message . '</p></div>';
+	}
+
+	public function get_text_domain() {
+		return $this->text_domain;
+	}
+
+	/**
+	 * @param string $min_version
+	 *
+	 * @return bool
+	 */
+	public static function is_wp_at_least( $min_version ) {
+		return version_compare( get_bloginfo( 'version' ), $min_version, '>=' );
+	}
+
 	/**
 	 * Are plugins loaded so we can check the version
 	 *
 	 * @return bool
 	 */
 	private function can_check_plugin_version() {
-		return did_action('plugins_loaded') > 0;
+		return did_action( 'plugins_loaded' ) > 0;
 	}
 
 	/**
@@ -192,10 +233,59 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 	 * @return bool
 	 */
 	public static function is_wc_at_least( $min_version ) {
-		return defined('WC_VERSION') &&
+		return defined( 'WC_VERSION' ) &&
 		       version_compare( WC_VERSION, $min_version, '>=' );
 	}
 
+	/**
+	 * Checks if ssl version is valid
+	 *
+	 * @param int $required_version Version in hex. Version 9.6 is 0x000906000
+	 *
+	 * @see https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html
+	 *
+	 * @return bool
+	 */
+	public static function is_open_ssl_at_least( $required_version ) {
+		return defined( 'OPENSSL_VERSION_NUMBER' ) && OPENSSL_VERSION_NUMBER > (int) $required_version;
+	}
+
+	/**
+	 * @param array $notices
+	 *
+	 * @return array
+	 */
+	private function append_plugin_require_notices( $notices ) {
+		if ( count( $this->plugin_require ) > 0 ) {
+			foreach ( $this->plugin_require as $plugin_name => $nice_plugin_name ) {
+				if ( ! $this->is_wp_plugin_active( $plugin_name ) ) {
+					$notices[] = $this->prepare_notice_message( sprintf( __( 'The &#8220;%s&#8221; plugin cannot run without %s active. Please install and activate %s plugin.',
+						$this->get_text_domain() ), esc_html( $this->plugin_name ),
+						esc_html( basename( $nice_plugin_name ) ), esc_html( basename( $nice_plugin_name ) ) ) );
+				}
+			}
+		}
+
+		return $notices;
+	}
+
+	/**
+	 * Checks if plugin is active. Needs to be enabled in deferred way.
+	 *
+	 * @param string $plugin_file
+	 *
+	 * @return bool
+	 */
+	public static function is_wp_plugin_active( $plugin_file ) {
+		$active_plugins = (array) get_option( 'active_plugins', array() );
+
+		if ( is_multisite() ) {
+			$active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
+		}
+
+		return in_array( $plugin_file, $active_plugins ) || array_key_exists( $plugin_file, $active_plugins );
+	}
+
 	/**
 	 * @param array $notices
 	 *
@@ -215,6 +305,15 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		return $notices;
 	}
 
+	/**
+	 * @param string $name
+	 *
+	 * @return bool
+	 */
+	public static function is_module_active( $name ) {
+		return extension_loaded( $name );
+	}
+
 	/**
 	 * @param array $notices
 	 *
@@ -234,25 +333,6 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		return $notices;
 	}
 
-	/**
-	 * @param array $notices
-	 *
-	 * @return array
-	 */
-	private function append_plugin_require_notices( $notices ) {
-		if ( count( $this->plugin_require ) > 0 ) {
-			foreach ( $this->plugin_require as $plugin_name => $nice_plugin_name ) {
-				if ( ! $this->is_wp_plugin_active( $plugin_name ) ) {
-					$notices[] = $this->prepare_notice_message( sprintf( __( 'The &#8220;%s&#8221; plugin cannot run without %s active. Please install and activate %s plugin.',
-						$this->get_text_domain() ), esc_html( $this->plugin_name ),
-						esc_html( basename( $nice_plugin_name ) ), esc_html( basename( $nice_plugin_name ) ) ) );
-				}
-			}
-		}
-
-		return $notices;
-	}
-
 	/**
 	 * @param string $name
 	 * @param mixed $value
@@ -263,21 +343,12 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		return ini_get( $name ) === strval( $value );
 	}
 
-	/**
-	 * @param string $name
-	 *
-	 * @return bool
-	 */
-	public static function is_module_active( $name ) {
-		return extension_loaded( $name );
-	}
-
 	/**
 	 * @return void
 	 */
 	public function disable_plugin_render_notice() {
-		add_action( 'admin_notices', array( $this, 'deactivate_action' ) );
-		add_action( 'admin_notices', array( $this, 'render_notices_action' ) );
+		add_action( self::HOOK_ADMIN_NOTICES_ACTION, array( $this, 'deactivate_action' ) );
+		add_action( self::HOOK_ADMIN_NOTICES_ACTION, array( $this, 'render_notices_action' ) );
 	}
 
 	/**
@@ -291,17 +362,6 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		}
 	}
 
-	/**
-	 * Prepares message in html format
-	 *
-	 * @param string $message
-	 *
-	 * @return string
-	 */
-	private function prepare_notice_message( $message ) {
-		return '<div class="error"><p>' . $message . '</p></div>';
-	}
-
 	/**
 	 * @return void
 	 */
@@ -311,51 +371,4 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable {
 		}
 	}
 
-	/**
-	 * @param $min_version
-	 *
-	 * @return mixed
-	 */
-	public static function is_php_at_least( $min_version ) {
-		return version_compare( phpversion(), $min_version, '>=' );
-	}
-
-	/**
-	 * @param string $min_version
-	 *
-	 * @return bool
-	 */
-	public static function is_wp_at_least( $min_version ) {
-		return version_compare( get_bloginfo( 'version' ), $min_version, '>=' );
-	}
-
-	/**
-	 * Checks if plugin is active. Needs to be enabled in deferred way.
-	 *
-	 * @param string $plugin_file
-	 *
-	 * @return bool
-	 */
-	public static function is_wp_plugin_active( $plugin_file ) {
-		$active_plugins = (array) get_option( 'active_plugins', array() );
-
-		if ( is_multisite() ) {
-			$active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
-		}
-
-		return in_array( $plugin_file, $active_plugins ) || array_key_exists( $plugin_file, $active_plugins );
-	}
-
-	/**
-	 * Checks if ssl version is valid
-	 *
-	 * @param int $required_version Version in hex. Version 9.6 is 0x000906000
-	 * @see https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html
-	 *
-	 * @return bool
-	 */
-	public static function is_open_ssl_at_least( $required_version ) {
-		return defined( 'OPENSSL_VERSION_NUMBER' ) && OPENSSL_VERSION_NUMBER > $required_version;
-	}
-
 }
diff --git a/src/Buildable.php b/src/Buildable.php
index 5e38cbd8ee88d061428946632f0626a0795bc78b..78c73224961af9592e346b83fd58f360d06971fa 100644
--- a/src/Buildable.php
+++ b/src/Buildable.php
@@ -1,7 +1,8 @@
 <?php
 
 /**
- * Have info about what class should be built
+ * Have info about what class should be built by WPDesk_Builder
+ *
  * have to be compatible with PHP 5.2.x
  */
 interface WPDesk_Buildable {
diff --git a/src/Plugin/Has_Plugin_Info.php b/src/Plugin/Has_Plugin_Info.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd4168ecaf9fbf45911f76a75e23b12d567b4357
--- /dev/null
+++ b/src/Plugin/Has_Plugin_Info.php
@@ -0,0 +1,29 @@
+<?php
+
+if (!class_exists('WPDesk_Translable')) {
+	require_once '../Translable.php';
+}
+
+
+/**
+ * Have MUST HAVE info for plugin instantion
+ *
+ * have to be compatible with PHP 5.2.x
+ */
+interface WPDesk_Has_Plugin_Info extends WPDesk_Translable {
+	/**
+	 * @return string
+	 */
+	public function get_plugin_file_name();
+
+	/**
+	 * @return string
+	 */
+	public function get_plugin_dir();
+
+	/**
+	 * @return string
+	 */
+	public function get_version();
+
+}
\ No newline at end of file
diff --git a/src/Plugin_Info.php b/src/Plugin/Plugin_Info.php
similarity index 91%
rename from src/Plugin_Info.php
rename to src/Plugin/Plugin_Info.php
index fca4beb327158c2e772d4d2b5ed1fea3ae146d0d..29a545998568ad7318700b139780f03c92ccbdc5 100644
--- a/src/Plugin_Info.php
+++ b/src/Plugin/Plugin_Info.php
@@ -1,17 +1,21 @@
 <?php
 
 if (!class_exists('WPDesk_Translable')) {
-	require_once 'Translable.php';
+	require_once '../Translable.php';
 }
 if (!class_exists('WPDesk_Buildable')) {
-	require_once 'Buildable.php';
+	require_once '../Buildable.php';
+}
+if (!class_exists('WPDesk_Has_Plugin_Info')) {
+	require_once 'Has_Plugin_Info.php';
 }
 
 /**
  * Structure with core info about plugin
+ *
  * have to be compatible with PHP 5.2.x
  */
-class WPDesk_Plugin_Info implements WPDesk_Translable, WPDesk_Buildable {
+class WPDesk_Plugin_Info implements WPDesk_Translable, WPDesk_Buildable, WPDesk_Has_Plugin_Info {
 	/** @var string */
 	private $plugin_file_name;
 
diff --git a/src/Translable.php b/src/Translable.php
index 3762dcf4b49c574144fff8761a74fd2c940eff17..32075f503d06cd35636bada1420cc2c751109c03 100644
--- a/src/Translable.php
+++ b/src/Translable.php
@@ -1,7 +1,8 @@
 <?php
 
 /**
- * Have info about textdomain
+ * Have info about textdomain - how to translate texts
+ *
  * have to be compatible with PHP 5.2.x
  */
 interface WPDesk_Translable  {
diff --git a/tests/unit/Test_Basic_Requirement_Checker.php b/tests/unit/Test_Basic_Requirement_Checker.php
new file mode 100644
index 0000000000000000000000000000000000000000..f7c2be96650ea921ece4cf39d9b9a398302bc544
--- /dev/null
+++ b/tests/unit/Test_Basic_Requirement_Checker.php
@@ -0,0 +1,176 @@
+<?php
+
+class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase {
+	const RANDOM_PLUGIN_FILE = 'file';
+
+	const RANDOM_PLUGIN_NAME = 'name';
+
+	const RANDOM_PLUGIN_TEXTDOMAIN = 'text';
+
+	const ALWAYS_VALID_PHP_VERSION = '5.2';
+
+	const ALWAYS_NOT_VALID_PHP_VERSION = '100.100';
+
+	const ALWAYS_VALID_WP_VERSION = '4.0';
+
+	const HOOK_TYPE_ACTION = 'action';
+
+	public function setUp() {
+		WP_Mock::setUp();
+
+		WP_Mock::wpFunction( 'get_bloginfo' )
+		       ->andReturn( self::ALWAYS_VALID_WP_VERSION );
+	}
+
+	public function tearDown() {
+		WP_Mock::tearDown();
+	}
+
+	public function test_php_version_check() {
+		$known_PHP_versions = [ '7.3', '7.2', '7.1', '7.0', '5.6', '5.5', '5.4', '5.3', '5.2' ];
+
+		$requirements = $this->create_requirements_for_php_wp(
+			self::ALWAYS_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		foreach ( $known_PHP_versions as $version ) {
+			$requirements->set_min_php_require( $version );
+			if ( version_compare( PHP_VERSION, $version, '>=' ) ) {
+				$this->assertTrue( $requirements->are_requirements_met(),
+					'Should be ok because WP is OK and PHP is OK' );
+			} else {
+				$this->assertFalse( $requirements->are_requirements_met(),
+					'Should fail because required PHP should be at least  ' . $version );
+			}
+		}
+		$requirements->set_min_php_require( self::ALWAYS_NOT_VALID_PHP_VERSION );
+		$requirements->are_requirements_met();
+		$this->expectOutputRegex( "/PHP/" );
+		$requirements->render_notices_action();
+	}
+
+	/**
+	 * @param string $php
+	 * @param string $wp
+	 *
+	 * @return WPDesk_Basic_Requirement_Checker
+	 */
+	public function create_requirements_for_php_wp( $php, $wp ) {
+		return new WPDesk_Basic_Requirement_Checker( self::RANDOM_PLUGIN_FILE, self::RANDOM_PLUGIN_NAME,
+			self::RANDOM_PLUGIN_TEXTDOMAIN, $php, $wp );
+	}
+
+	public function test_wp_version_check() {
+		$wp_version_fail = '4.1';
+
+		$requirements = $this->create_requirements_for_php_wp(
+			self::ALWAYS_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		$this->assertTrue( $requirements->are_requirements_met(), 'Should be ok because WP is OK and PHP is OK' );
+		$requirements->set_min_wp_require( $wp_version_fail );
+		$this->assertFalse( $requirements->are_requirements_met(),
+			'Should fail because required WP should be at least ' . $wp_version_fail );
+
+		$this->expectOutputRegex( "/WordPress/" );
+		$requirements->render_notices_action();
+	}
+
+	/**
+	 * @requires extension curl
+	 */
+	public function test_module_check() {
+		$requirements = $this->create_requirements_for_php_wp(
+			self::ALWAYS_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		$requirements->add_php_module_require( 'curl' );
+		$this->assertTrue( $requirements->are_requirements_met(), 'Curl should exists' );
+
+		$this->expectOutputRegex( "/^$/" );
+		$requirements->render_notices_action();
+	}
+
+	public function test_plugin_check_with_multisite() {
+		$multisite                     = true;
+		$exising_plugin_name           = 'WooCommerce';
+		$exising_multisite_plugin_name = 'Multisite';
+		$not_existing_plugin_name      = 'Not exist';
+
+		WP_Mock::wpFunction( 'get_option' )
+		       ->withArgs( [ 'active_plugins', [] ] )
+		       ->andReturn( [ $exising_plugin_name ] );
+
+		WP_Mock::wpFunction( 'is_multisite' )
+		       ->andReturn( $multisite );
+
+		WP_Mock::wpFunction( 'get_site_option' )
+		       ->withArgs( [ 'active_sitewide_plugins', [] ] )
+		       ->andReturn( [ $exising_multisite_plugin_name ] );
+
+
+		$requirements = $this->create_requirements_for_php_wp( self::ALWAYS_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		$requirements->add_plugin_require( $exising_plugin_name );
+		$this->assertTrue( $requirements->are_requirements_met(), 'Plugin should exists' );
+
+		$requirements->add_plugin_require( $exising_multisite_plugin_name );
+		$this->assertTrue( $requirements->are_requirements_met(), 'Multisite plugin should exists' );
+
+		$requirements->add_plugin_require( $not_existing_plugin_name );
+		$this->assertFalse( $requirements->are_requirements_met(), 'Plugin should not exists' );
+
+		$this->expectOutputRegex( "/$not_existing_plugin_name/" );
+		$requirements->render_notices_action();
+	}
+
+	/**
+	 * @requires extension openssl
+	 */
+	public function test_existing_openssl_requirement() {
+		$open_ssl_always_valid     = 1;
+		$open_ssl_always_not_valid = 0x900905000; // 9.9.6
+
+		$requirements = $this->create_requirements_for_php_wp( self::ALWAYS_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		$this->assertTrue( $requirements->is_open_ssl_at_least( $open_ssl_always_valid ),
+			'OpenSSL should have at least 0.1 version if exists' );
+
+		$this->assertFalse( $requirements->is_open_ssl_at_least( $open_ssl_always_not_valid ),
+			'OpenSSL should fail for that high number' );
+
+		$requirements->set_min_openssl_require( $open_ssl_always_not_valid );
+
+		$this->assertFalse( $requirements->are_requirements_met(),
+			'Requirement OpenSSL should fail for that high number' );
+
+		$this->expectOutputRegex( '/without OpenSSL module/' );
+		$requirements->render_notices_action();
+	}
+
+	public function test_deactivate_plugin_notice() {
+		$requirements = $this->create_requirements_for_php_wp( self::ALWAYS_NOT_VALID_PHP_VERSION,
+			self::ALWAYS_VALID_WP_VERSION );
+
+		WP_Mock::expectActionAdded( WPDesk_Basic_Requirement_Checker::HOOK_ADMIN_NOTICES_ACTION,
+			[ $requirements, 'deactivate_action' ] );
+		WP_Mock::expectActionAdded( WPDesk_Basic_Requirement_Checker::HOOK_ADMIN_NOTICES_ACTION,
+			[ $requirements, 'render_notices_action' ] );
+
+		$this->assertFalse( $requirements->are_requirements_met() );
+		$requirements->disable_plugin_render_notice();
+
+		WP_Mock::wpFunction( 'deactivate_plugins' )
+		       ->once();
+
+		WP_Mock::wpFunction( 'plugin_basename' )
+		       ->once()
+		       ->andReturn( 'whatever' );
+
+		$this->expectOutputRegex( '/cannot run on PHP/' );
+		$requirements->deactivate_action();
+		$requirements->render_notices_action();
+	}
+}
diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..e55059f69f9265d81aa3a4afa8c679633a5a6ad9
--- /dev/null
+++ b/tests/unit/bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * PHPUnit bootstrap file
+ */
+
+require_once __DIR__ . '/../../vendor/autoload.php';
+
+error_reporting( E_ALL );
+
+if ( getenv( 'PLUGIN_PATH' ) !== false ) {
+	define( 'PLUGIN_PATH', getenv( 'PLUGIN_PATH' ) );
+} else {
+	define( 'PLUGIN_PATH', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR );
+}
+
+if ( getenv( 'ABSPATH' ) !== false ) {
+	define( 'ABSPATH', getenv( 'ABSPATH' ) );
+} else {
+	define( 'ABSPATH', PLUGIN_PATH . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR );
+}
+
+WP_Mock::setUsePatchwork( true );
+WP_Mock::bootstrap();