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

Adding Unit testing using Codeception #26

Open
pamtbaau opened this issue Mar 20, 2023 · 10 comments
Open

Adding Unit testing using Codeception #26

pamtbaau opened this issue Mar 20, 2023 · 10 comments
Assignees

Comments

@pamtbaau
Copy link
Collaborator

pamtbaau commented Mar 20, 2023

Updated 2023-08-21

The following will add Unit tests using Codeception.

I don't know how you've setup your dev environment. I've separated themes/plugins I develop into folders outside the root of Grav and use symlinks to add the plugin.

  • Create fresh install of Grav + Admin

  • Add pushy as plugin (either regular or symlinked)

  • $ cd user/plugins/pushy

  • Edit composer.json to use higher PHP versions required by Grav:

    "require": {
      "php": ">=7.3.6 || ^8.0",
    
    "config": {
      "platform": {
        "php": "7.3.6"
    
  • $ composer require --dev codeception/codeception

  • $ vendor/bin/codecept bootstrap

    • Allow composer to add two required packages
  • $ vendor/bin/codecept generate:test unit Pushy

  • Copy the following into /tests/unit/PushyTests.php.
    The code will:

    • Before each test:
      • Erase all artefacts
      • Setup files that are being added, modified, deleted and renamed/moved.
    • Fetches all changed files and check if result matches expected result
    • Submit a set of changes to the server and checks if a success has been returned.
<?php

use function PHPUnit\Framework\assertEquals;
use function PHPUnit\Framework\assertTrue;

class PushyTest extends \Codeception\Test\Unit
{
    public $files = [
        '/www/grav/site-pushy/user/pages/03.added/default.md',
        '/www/grav/site-pushy/user/pages/04.modified/default.md',
        '/www/grav/site-pushy/user/pages/05.deleted/default.md',
        '/www/grav/site-pushy/user/pages/06.rename_old/default.md',
    ];

    public $folders = [
        '/www/grav/site-pushy/user/pages/03.added',
        '/www/grav/site-pushy/user/pages/04.modified',
        '/www/grav/site-pushy/user/pages/05.deleted',
        '/www/grav/site-pushy/user/pages/06.rename_old',
    ];

    /**
     * @var \UnitTester
     */
    protected $tester;

    protected function _before()
    {
        $this->setupFiles();
    }

    protected function _after()
    {
    }

    // tests
    public function testReadItems()
    {
        $setopt_content = [];

        $setopt_content[] = TRUE;
        $ch = curl_init('http://localhost/grav/site-pushy/admin/publish/pushy:readItems');

        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $result_data = curl_exec($ch);
        curl_close($ch);

        $result = json_decode($result_data, true);
        codecept_debug($result);

        assertEquals($result[0]['index'], 'A');
        assertEquals($result[0]['path'], 'pages/03.added/default.md');

        assertEquals($result[1]['index'], 'M');
        assertEquals($result[1]['path'], 'pages/04.modified/default.md');

        assertEquals($result[2]['index'], 'D');
        assertEquals($result[2]['path'], 'pages/05.deleted/default.md');

        assertEquals($result[3]['index'], 'R');
        assertEquals($result[3]['path'], 'pages/06.rename_new/default.md');
        assertEquals($result[3]['orig_path'], 'pages/06.rename_old/default.md');
    }

    public function testPublishItems()
    {
        $payload = [
            'items' => [
                [
                    'index' => 'A',
                    'path' => 'pages/03.added/default.md',
                ],
                [
                    'index' => 'M',
                    'path' => 'pages/04.modified/default.md',
                ],
                [
                    'index' => 'D',
                    'path' => 'pages/05.deleted/default.md',
                ],
                [
                    'index' => 'R',
                    'orig_path' => 'pages/06.rename_old/default.md',
                    'path' => 'pages/06.rename_new/default.md',
                ],
            ],
            "message" => "Publish changed pages",
        ];


        $ch = curl_init('http://localhost/grav/site-pushy/admin/publish/pushy:publishItems');

        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $result_data = curl_exec($ch);
        curl_close($ch);

        $result = json_decode($result_data, true);
        codecept_debug($result);

        assertTrue($result['isSuccess']);
        assertEquals($result['alert'], 'Items have been published.');
    }

    private function setupFiles()
    {
        $this->clearFiles();
        $this->createFiles();
        $this->commitFiles();
        $this->modifyFile();
        $this->deleteFile();
        $this->renameFile();
    }

    private function clearFiles()
    {
        exec('cd /www/grav/site-pushy/user && rm -rf .git');

        foreach ($this->folders as $folder) {
            exec('cd /www/grav/site-pushy/user && rm -rf ' . $folder);
        }

        exec('cd /www/grav/site-pushy/user && rm -rf pages/06.rename_new');

        exec('cd /www/grav/site-pushy/user && git init');
        exec('cd /www/grav/site-pushy/user && git config user.name "unit" && git config user.email "[email protected]"');
        exec('cd /www/grav/site-pushy/user && git add . && git commit -m "initial commit"');
    }

    private function createFiles()
    {
        foreach ($this->folders as $folder) {
            mkdir($folder, 0777, true);
        }

        file_put_contents(
            '/www/grav/site-pushy/user/pages/03.added/default.md',
            "---\ntitle: Added\n---",
        );
        file_put_contents(
            '/www/grav/site-pushy/user/pages/04.modified/default.md',
            "---\ntitle: To be modified\n---",
        );
        file_put_contents(
            '/www/grav/site-pushy/user/pages/05.deleted/default.md',
            "---\ntitle: To be deleted\n---",
        );
        file_put_contents(
            '/www/grav/site-pushy/user/pages/06.rename_old/default.md',
            "---\ntitle: To be renamed\n---",
        );
    }

    public function commitFiles()
    {
        exec('cd /www/grav/site-pushy/user && git add pages/04.modified/default.md pages/05.deleted/default.md pages/06.rename_old/default.md');
        exec('cd /www/grav/site-pushy/user && git commit -m "Initial commit"');
    }

    private function modifyFile()
    {
        file_put_contents(
            '/www/grav/site-pushy/user/pages/04.modified/default.md',
            "\n# Content title",
            FILE_APPEND,
        );
    }

    private function deleteFile()
    {
        exec('cd /www/grav/site-pushy/user && rm -rf pages/05.deleted');
    }

    private function renameFile()
    {
        exec('cd /www/grav/site-pushy/user && rm -rf pages/06.rename_new');
        exec('cd /www/grav/site-pushy/user && mv -f pages/06.rename_old/ pages/06.rename_new');
    }
}

Run the following commands:

  • To run all defined unit tests
    $ vendor/bin/codecept run --debug unit
  • To run all test defined in single file
    $ vendor/bin/codecept run --debug unit PushyTest
  • To run a single test method
    $ vendor/bin/codecept run --debug unit PushyTest::testReadItems
    $ vendor/bin/codecept run --debug unit PushyTest::testPublishItems
@hughbris hughbris self-assigned this Mar 21, 2023
@hughbris
Copy link
Owner

Thank you, this will be very helpful.

I assume _before() runs before any test, so I'd need to hook this up with a disposable installation because of clearFiles(). Can do.

Because I use Docker, I'll need to persist that unit tests folder so I don't lose PushyTest.php after every container rebuild. That's also not difficult.

I'll have to tweak some of the hardcoded PushyTest.php paths and local URLs too.

I am thinking the simplest way for me to run these development tools in my development installs is to create a development tag on my Docker Grav platform image. The image can include this and #25, and eventually my Xdebug integration (when I get it working!).

Great tools!

I'll let you know when I've run these tests successfully and presumably you can close the issue after that.

@pamtbaau
Copy link
Collaborator Author

pamtbaau commented Mar 23, 2023 via email

@hughbris
Copy link
Owner

I'm running $ vendor/bin/codecept run --debug unit PushyTest::testReadItems from your top post instructions and see the test run, and this summary:

There was 1 error:

---------
1) PushyTest: Read items
 Test  tests/unit/PushyTest.php:testReadItems
                                                                            
  [TypeError] array_merge(): Argument #1 must be of type array, null given  
                                                                            
#1  /var/www/grav/user/shared/plugins/pushy/vendor/bin/codecept:119

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

(Initially I ran all unit tests and also saw this message for the Publish Items test.)

I've tweaked your code snippet for working paths. I've confirmed the URL used retrieving /admin/publish/pushy:readItems results in valid JSON when using curl at CLI.

I changed into the plugin directory before running the composer commands.

I suspect I missed or did something wrong setting this up.

@pamtbaau
Copy link
Collaborator Author

I've update the instructions in first post.

  • I've add a bit more specificity
  • Upgraded composer to upgrade Grav required min versions. There have been problems with 3rd party plugins not running correctly with older min versions.

@hughbris
Copy link
Owner

Thanks. I'm still seeing the same error, unfortunately.

I don't know how you've setup your dev environment. I've separated themes/plugins I develop into folders outside the root of Grav and use symlinks to add the plugin.

My setup is very similar. I usually keep plugins/themes, that are shared or I hack on, in their own path and symlink them as required. It's a bit more complicated than that, but effectively that is a working setup. Symlinks generally seem transparent although I have noticed traversing using parent expressions like .. is unreliable. So far this has never been an issue running a Grav install. I did notice the line #119 referenced in the error tries to include a parent-relative file.

Going back a step, I actually finally got to trying out your instructions here because I am building a new plugin and wanted to drive it with tests from the start (TDD!). This plugin currently sits under a site's user/plugins without using a symlink. Sometimes I do that to start off and then move it and symlink it later. (Also because I create it using devtools.)

I think next I will try your instructions in this new plugin with a dummy test and see if I have more luck there. Thanks for your patient explanations!

@hughbris
Copy link
Owner

I think next I will try your instructions in this new plugin with a dummy test and see if I have more luck there.

I was able to run a dummy unit test, but hit other problems when I tried to assert anything. Examples in Codeception docs are also throwing errors. Seems something is not bootstrapping or loading properly. I need to move onto other tasks. I will contact you offline because it's a bit tedious troubleshooting this here.

@pamtbaau
Copy link
Collaborator Author

When updating the setup script shown above an error was thrown about not existing assertEquals and assertTrue methods. Updating the PHP requirements in composer.json solved the issue.

"require": {
  "php": ">=7.3.6 || ^8.0",

"config": {
  "platform": {
    "php": "7.3.6"

@hughbris
Copy link
Owner

Updating the PHP requirements in composer.json solved the issue.

I had made that change and it's not solving it for me :/

@pamtbaau
Copy link
Collaborator Author

Just to be sure, a setup with a fresh copy of Grav and fresh plugin...

In my previous tests, an older version of Codeception 4.2 was lingering around while 5.0 is out. I presume your dev environment is PHP 8.x

Setup:

  • Download and install latest Grav
  • cd into root of grav
  • $ bin/gpm install devtools
  • $ bin/plugin devtools new-plugin create new blank plugin called pushy
  • $ cd user/plugins/pushy
  • $ composer update
  • $ composer require --dev codeception/codeception
    Version 5.0 will be installed.
  • $ vendor/bin/codecept bootstrap
    Accept installation of 2 extra packages
  • $ vendor/bin/codecept generate:test Unit Pushy
  • $ vendor/bin/codecept run
    All should be well and result should show in green OK (1 test, 0 assertions)
  • Edit file tests/Unit/PushyTest.php and add assertTrue(1 === 1); inside method testSomeFeature()
    Make sure to import assertTrue
  • $ vendor/bin/codecept run
    All should be well and result should show in green OK (1 test, 1 assertion)

Note, no need to change any PHP version in composer.json inside the plugin.

@hughbris
Copy link
Owner

hughbris commented Sep 4, 2023

Thanks. I finally got to testing this and it worked with the clean install.

I ran the previous tests on "clean" installs, except I reused/persisted user, backups, and logs. That's a simplification because I use docker, but it's effectively the same. My successful test just then also used docker, just without populating those directories. So it's more likely to be something in my user folder than docker that's causing my error, I think.

I think (from faulty memory) I have also tried this with another instance/container and got the same result. Let me try some others and get back to you.

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