Brieuc
Brieuc

Reputation: 4114

Unit Tests with a custom wordpress plugin having nested plugin dependencies

So I created a custom plugin and implemented unit tests.

So far, it has been easy to add a WooCommerce dependency and a private plugin dependency (Iconic Plugin).

The problem is that Iconic plugin is dependent on WooCommerce. At each test, it thinks that WooCommerce is not activated.

Therefore, it does not instantiate correctly.

Iconic plugin

class Iconic_Private_Plugin() {

    /**
     * Constructor
     */
    public function __construct() {

        public $bar;

        if ( ! Iconic_Private_Core_Helpers::is_plugin_active( 'woocommerce/woocommerce.php' ) && ! Iconic_Private_Core_Helpers::is_plugin_active( 'woocommerce-old/woocommerce.php' ) ) {
            return;
            // It stops right here!!!
        }

        $this->bar = "foo"; // Not assigned!!!

    }

}
global $iconic_private_plugin; // Methods can be accessed from global variable $iconic_private_plugin.
$iconic_private_plugin = new Iconic_Private_Plugin();

So here what I did from bash commands, bootstrap & unit tests.

install-wp-tests.sh

install_dependencies() {
    WP_SITE_URL="http://localhost:8080"
    WP_PLUGIN_DIR=$(pwd)
    WP_DB_DATA="$WP_PLUGIN_DIR/tests/data/db.sql"

    cd "$WP_CORE_DIR"
    curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
    php wp-cli.phar core config --dbname=$DB_NAME --dbuser=$DB_USER --dbpass=$DB_PASS --dbhost=$DB_HOST --dbprefix=wptests_
    php wp-cli.phar db import $WP_DB_DATA
    php wp-cli.phar search-replace "http://local.wordpress.test" "$WP_SITE_URL"
    php wp-cli.phar theme install twentyseventeen --activate
    php wp-cli.phar plugin install https://downloads.wordpress.org/plugin/woocommerce.${WC_VERSION}.zip --activate
    php wp-cli.phar plugin install https://downloads.wordpress.org/plugin/posts-to-posts.${P2P_VERSION}.zip --activate
    php wp-cli.phar plugin install $WP_PLUGIN_DIR/plugins/iconic-private-plugin.zip --activate
    php wp-cli.phar plugin list
}

bootstrap.php

/**
 * Setup the unit testing environment.
 */
public function __construct() {
    // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions, WordPress.PHP.DevelopmentFunctions
    ini_set( 'display_errors', 'on' );
    error_reporting( E_ALL );
    // phpcs:enable WordPress.PHP.DiscouragedPHPFunctions, WordPress.PHP.DevelopmentFunctions

    // Ensure server variable is set for WP email functions.
    // phpcs:disable WordPress.VIP.SuperGlobalInputUsage.AccessDetected
    if ( ! isset( $_SERVER['SERVER_NAME'] ) ) {
        $_SERVER['SERVER_NAME'] = 'localhost';
    }
    // phpcs:enable WordPress.VIP.SuperGlobalInputUsage.AccessDetected

    $this->tests_dir    = dirname( __FILE__ );
    $this->plugin_dir   = dirname( $this->tests_dir );
    $this->wp_tests_dir = getenv( 'WP_TESTS_DIR' ) ? getenv( 'WP_TESTS_DIR' ) : rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';

    // load test function so tests_add_filter() is available.
    require_once $this->wp_tests_dir . '/includes/functions.php';

    // load Dependencies.
    tests_add_filter( 'muplugins_loaded', array( $this, 'my_custom_plugin_manually_load_plugin' ) );

    // install WC.
    tests_add_filter( 'setup_theme', array( $this, 'install_wc' ) );

    // load the WP testing environment.
    require_once $this->wp_tests_dir . '/includes/bootstrap.php';
}

/**
 * Install WooCommerce after the test environment and WC have been loaded.
 */
public function install_wc() {
    // Clean existing install first.
    define( 'WP_UNINSTALL_PLUGIN', true );
    define( 'WC_REMOVE_ALL_DATA', true );
    include (ABSPATH . 'wp-content/plugins/woocommerce/uninstall.php');
    WC_Install::install();
    // Reload capabilities after install, see https://core.trac.wordpress.org/ticket/28374
    if ( version_compare( $GLOBALS['wp_version'], '4.7', '<' ) ) {
        $GLOBALS['wp_roles']->reinit();
    } else {
        $GLOBALS['wp_roles'] = null; // WPCS: override ok.
        wp_roles();
    }
    echo esc_html( 'Installing WooCommerce...' . PHP_EOL );
}


/**
 * Load Dependencies
 */
public function my_custom_plugin_manually_load_plugin() {
    $this->plugins_dir = ABSPATH . str_replace( site_url() . '/', '', plugins_url() ) . '/';

    // Load dependencies.
    require_once $this->plugins_dir . 'woocommerce/woocommerce.php';
    require_once $this->plugins_dir . 'iconic/iconic-private-plugin.php';

    // Load plugin.
    require_once $this->plugin_dir . '/my-custom-plugin.php';

    global $my_custom_plugin;
    $my_custom_plugin = new My_Custom_Plugin_Class();
}

My unit tests

<?php
/**
 * Sql test case.
 */
class SqlTest extends WP_UnitTestCase {

    /**
     * Set up fixtures before class for all tests in SqlTest Class.
     *
     * @param object $factory passed by WP_UnitTestCase.
     */
    public static function wpSetUpBeforeClass( $factory ) {
        self::createFixtures();
        self::createSettings();
    }

    public function setUp() {
        self::set_active_plugins();
    }

    public static function set_active_plugins() {
        activate_plugin(ABSPATH . 'wp-content/plugins/woocommerce/woocommerce.php');
        activate_plugin(ABSPATH . 'wp-content/plugins/iconic/iconic-private-plugin.php');
    }

    /**
     * Warehouses fixtures
     */
    public static function createFixtures() {
        // [...]
    }

    /**
     * Create timeslots
     */
    public static function createSettings() {
        $settings = array (
            // [...]
        );

        update_option( 'iconic_private_plugin_settings', $settings );
        global $iconic_private_plugin;
        $iconic_private_plugin->settings = $settings;
    }

    /**
     * Test for custom_function.
     */
    public function test_custom_function() {
        global $my_custom_plugin, $iconic_private_plugin;

        echo $iconic_private_plugin->bar; // Has not been assigned!!! (but it should be.

        print_r($iconic_private_plugin); 
        // Class properties are empty, since construct method stop at WooCommerce activation check. (same as above example)

        $this->assertTrue(true);
    }

}

As you can see I even tried to activate straight in the database the plugins between each test (setUp function).

So i guess the problem could come from bootstrap.php

I could not find any documentation or similar issue through the web.

The only thing that was close enough :

WordPress plugin phpunit tests with dependencies

I also get inspired from WooCommerce tests setup.

Any ideas of what i'm doing wrong ?

UPDATE

After T.Todua's answer i have some warnings thrown in the console. However it did solve the dependency's instantiation and tests are working as intended.

But since there are those warnings, i'm wondering if it is the right way to do it and/or how to fix those warnings.

All those warnings seem to be related to Freemius dependency (only putting one here since there are more or less the same).

So probably not related to this issue.

Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
PHP Warning:  fopen(/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/iconic-private-plugin.php): failed to open stream: No such file or directory in /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-includes/functions.php on line 4918
PHP Stack trace:
PHP   1. {main}() /usr/local/bin/phpunit:0
PHP   2. PHPUnit\TextUI\Command::main() /usr/local/bin/phpunit:570
PHP   3. PHPUnit\TextUI\Command->run() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:148
PHP   4. PHPUnit\TextUI\Command->handleArguments() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:159
PHP   5. PHPUnit\TextUI\Command->handleBootstrap() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:788
PHP   6. PHPUnit\Util\Fileloader::checkAndLoad() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:991
PHP   7. PHPUnit\Util\Fileloader::load() phar:///usr/local/bin/phpunit/phpunit/Util/Fileloader.php:48
PHP   8. include_once() phar:///usr/local/bin/phpunit/phpunit/Util/Fileloader.php:64
PHP   9. My_Plugin_Unit_Tests_Bootstrap::instance() /Users/brieuc/Workspace/php/my-plugin/web/wp-content/plugins/my-plugin-geo/tests/bootstrap.php:150
PHP  10. My_Plugin_Unit_Tests_Bootstrap->__construct() /Users/brieuc/Workspace/php/my-plugin/web/wp-content/plugins/my-plugin-geo/tests/bootstrap.php:145
PHP  11. require_once() /Users/brieuc/Workspace/php/my-plugin/web/wp-content/plugins/my-plugin-geo/tests/bootstrap.php:80
PHP  12. require_once() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress-tests-lib/includes/bootstrap.php:105
PHP  13. do_action() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-settings.php:295
PHP  14. WP_Hook->do_action() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-includes/plugin.php:453
PHP  15. WP_Hook->apply_filters() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-includes/class-wp-hook.php:310
PHP  16. My_Plugin_Unit_Tests_Bootstrap->load_wds() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-includes/class-wp-hook.php:286
PHP  17. require_once() /Users/brieuc/Workspace/php/my-plugin/web/wp-content/plugins/my-plugin-geo/tests/bootstrap.php:93
PHP  18. jckWooDeliverySlots->__construct() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/iconic-private-plugin.php:3209
PHP  19. jckWooDeliverySlots->load_classes() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/iconic-private-plugin.php:114
PHP  20. Iconic_Private_Core_Licence::run() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/iconic-private-plugin.php:175
PHP  21. Iconic_Private_Core_Licence->__construct() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/class-core-licence.php:49
PHP  22. Iconic_Private_Core_Licence::configure_freemius() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/class-core-licence.php:60
PHP  23. fs_dynamic_init() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/class-core-licence.php:88
PHP  24. Freemius::instance() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/start.php:506
PHP  25. Freemius->__construct() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/includes/class-freemius.php:2217
PHP  26. Freemius->get_plugin_name() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/includes/class-freemius.php:469
PHP  27. Freemius->set_name() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/includes/class-freemius.php:8627
PHP  28. Freemius->get_plugin_data() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/includes/class-freemius.php:8648
PHP  29. get_plugin_data() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-content/plugins/iconic-private-plugin/inc/vendor/freemius/includes/class-freemius.php:8475
PHP  30. get_file_data() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-admin/includes/plugin.php:84
PHP  31. fopen() /private/var/folders/1n/7zbwzbgj1vz7y89rbjb5hg340000gn/T/wordpress/wp-includes/functions.php:4918

Upvotes: 5

Views: 1506

Answers (1)

T.Todua
T.Todua

Reputation: 56497

what if you used

private function force_activate( $plugin ) {
    if( ! function_exists('activate_plugin') ) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }

    if( ! is_plugin_active( $plugin ) ) {
        activate_plugin( $plugin );
    }
}



...

// Load dependencies.
require_once $this->plugins_dir . 'woocommerce/woocommerce.php';
$this->force_activate('woocommerce/woocommerce.php');
require_once $this->plugins_dir . 'iconic/iconic-private-plugin.php';

Upvotes: 3

Related Questions