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

Added support for Codeception test framework #143

Closed
wants to merge 13 commits into from
Closed

Added support for Codeception test framework #143

wants to merge 13 commits into from

Conversation

tobiasstadler
Copy link

Fixes #45

@DavertMik
Copy link

Any updates?

@theofidry
Copy link
Member

Will try to review that in the course of next week, I'm sure the rest of the team will too. Thanks for the hard work and sorry to be a bit slow on this one :)

@theofidry theofidry requested review from maks-rafalko, corpsee, theofidry and sidz and removed request for corpsee February 3, 2018 09:33
Copy link
Member

@maks-rafalko maks-rafalko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your PR

  • Could you please rebase?
  • Could you please add an e2e test (see an example in tests/Fixtures/e2e) to prove it works with Codeception?

composer.json Outdated
@@ -46,7 +46,8 @@
},
"require-dev": {
"phpunit/phpunit": "^6.1",
"mockery/mockery": "^1.0"
"mockery/mockery": "^1.0",
"codeception/codeception": "^2.3.8"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please explain why is it needed?

phpstan.neon Outdated
- '#Call to an undefined method PhpParser\\Node(.*?)#'
- '#Call to an undefined method Mockery\\MockInterface(.*?)#'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please explain why is it needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

phpstan complains about a call to an undefined method Mockery\MockInterface::getMutation() in tests/TestFramework/Codeception/CommandLine/ArgumentsAndOptionsBuilderTest.php

@@ -74,12 +74,12 @@ public function __construct(
*
* @return string
*/
public function getExecutableCommandLine(string $configPath, string $extraOptions, bool $includePhpArgs = true): string
public function getExecutableCommandLine(string $configPath, string $extraOptions, bool $includePhpArgs = true, Mutant $mutant = null): string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I don't think adding Mutant parameter here is a good way. This is why we have 2 ConfigBuilder implementations: InitialConfigBuilder and MutationConfigBuilder - two classes for different cases rather than one class with if () {} else {}.

If you really need to build different options for Initial test run and for mutations - let's create two separate ArgumentAndOptionBuilders (initial/mutation) with different parameters.

But see below suggestions about avoiding changes in this interface

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still needs to be fixed

];

if ($mutant !== null) {
$options[] = '-o "paths: output: ' . $this->tempDir . '/' . $mutant->getMutation()->getHash() . '"';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please explain what is it used for?


public function build(): string
{
return $this->originalConfigPath;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's better to create all needed for Infection parameters in a new, temp config like it is done for PHPUnit and PHPSpec rather than adding/overriding config in ArgumentsAndOptionsBuilder?

This will probably help to avoid passing Mutant $mutant parameter to ArgumentsAndOptionsBuilder

In other words, I suggest to move this code:

$options[] = '-o "paths: output: ' . $this->tempDir . '"';
$options[] = '-o "coverage: enabled: true"';
$options[] = '--coverage-phpunit ' . CodeCoverageData::CODECEPTION_COVERAGE_DIR;

from ArgumentsAndOptionsBuilder here and create temp config. It will be consistent with the other Test Frameworks and will help to avoid changing the interface

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately Codeception does not handle absolute paths in its config files, but maybe I can get it working using relative paths.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest creating Codeception's Extension which will transform the current config the way you need it.
Extension can be published as a standalone package

$mutatedFilePath = $mutant->getMutatedFilePath();
$interceptorPath = dirname(__DIR__, 4) . '/StreamWrapper/IncludeInterceptor.php';

// TODO change to what it was (e.g. app/autoload - see simplehabits)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please do not copy todo comments


public function build(Mutant $mutant): string
{
$_SERVER['CUSTOM_AUTOLOAD_FILE_PATH'] = $customAutoloadFilePath = sprintf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please expand on why do you need it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed in order to autoload the mutated file. As far as I know Codeception does not support a custom bootstrap file (as e.g. PHPUnit)

Copy link
Member

@sidz sidz Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use putenv instead of global variables

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sidz actually putenv() and getenv() are not thread safe so might be better to do it that way. I would however add an INFECTION_ prefix or something to it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infection runs frameworks via symfony console which knows how to work with envs. We already use it :P.

p.s. IMHO Don't like to touch global variables

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to follow, I don't see how using the Console component helps to make it thread-safe here.

Maybe for reference: symfony/recipes#156

Also I don't like to touch global variables much, but that's exactly what put/getenv are doings and regardless of what we are using we should use a prefix here IMO


namespace Infection\TestFramework\Codeception;

use Codeception\Events;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. it will not work since codeception/codeception is in require-dev section and all files from this section are absent in PHAR distribution
  2. we need to find a way to not use Codeception dependency at all. Infection should not depend on any test framework.

could you please explain what is the goal of this file, we will try to find another way to achieve the same goal

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't thin this is a problem, because the code path is only used when the test framework is codeception.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is not really if this is going to work, but rather than each test framework supported must be instrumented as a blackbox with no access to their internal. There is several reasons for that:

  • It is easier to maintain on our side and things are less likely to break (the end-user API is usually more stable than the code API, public like internal)
  • As we are going to integrate Infection with PHP-Scoper to be able to provide an isolated PHAR, this kind of dependence won't work anymore easily

Copy link
Member

@sanmai sanmai Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could work if this file won't be a part of Infection. (Isn't that already so?) Instead you could tell Codeception to include a file from the outside Infection. E.g. there's an IncludeInterceptor which is added to the tested code at one point. I'm not entirely sure you could do what you want there, but there's an opportunity.

];
}

protected function tearDown()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extend Mockery\Adapter\Phpunit\MockeryTestCase file. No need to use this tearDown function anymore

throw new \InvalidArgumentException(
sprintf(
'Invalid name of test framework. Available names are: %s',
implode(', ', [TestFrameworkTypes::PHPUNIT, TestFrameworkTypes::PHPSPEC])
implode(', ', [TestFrameworkTypes::PHPUNIT, TestFrameworkTypes::PHPSPEC, TestFrameworkTypes::CODECEPTION])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please use TestFrameworkTypes::TYPES constant here?

@@ -69,7 +69,7 @@ protected function configure()
'test-framework',
null,
InputOption::VALUE_REQUIRED,
'Name of the Test framework to use (phpunit, phpspec)',
'Name of the Test framework to use (phpunit, phpspec, codeception)',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please use TestFrameworkTypes::TYPES constant here as we do in the ConfigureCommand?

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
if [[ $PHPDBG = 1 ]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@borNfreee told that he had an issue with this variant on his mac. Could you please change this line to the if [ "$PHPDBG" = "1" ] ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had an issue with if [[ $PHPDBG=1 ]] on my Mac. if [[ $PHPDBG = 1 ]] works for me.

@borNfreee Does it also work for you?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it does not work, see #151 (comment)

Copy link
Member

@BackEndTea BackEndTea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments, but overall this looks pretty good

tests/e2e_tests Outdated
@@ -19,7 +19,7 @@ do
composer install > /dev/null 2>&1
fi

if [ -f "run_tests.bash" ]
if [ -f "run_test.bash" ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you rebase on master please undo this change.

The intended filename is run_tests.bash, the then clause not running the correct file has been fixed on master.

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename the file to run_tests.bash

use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Yaml;

function rp(string $path) : string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move this function into the test class as a helper function

Copy link
Member

@theofidry theofidry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few notes on top of what the others already said.

Thanks again for the great work, it's a significant piece!

*/
private $tempDir;

public function __construct(string $tempDir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$tmpDir rather than $tempDir; same for the other places


public function build(Mutant $mutant): string
{
$_SERVER['CUSTOM_AUTOLOAD_FILE_PATH'] = $customAutoloadFilePath = sprintf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sidz actually putenv() and getenv() are not thread safe so might be better to do it that way. I would however add an INFECTION_ prefix or something to it


namespace Infection\TestFramework\Codeception;

use Codeception\Events;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is not really if this is going to work, but rather than each test framework supported must be instrumented as a blackbox with no access to their internal. There is several reasons for that:

  • It is easier to maintain on our side and things are less likely to break (the end-user API is usually more stable than the code API, public like internal)
  • As we are going to integrate Infection with PHP-Scoper to be able to provide an isolated PHAR, this kind of dependence won't work anymore easily

* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
*
* @SuppressWarnings(PHPMD)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this @SuppressWarnings(PHPMD)necessary?

modules:
enabled:
- Asserts
- \Helper\Unit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • missing line return
  • I don't think the leading \ is necessary is it?

/**
* @dataProvider passProvider
*/
public function test_it_determines_whether_tests_pass_or_not($output, $expectedResult)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(string $output, bool $expectedResult)


public function passProvider()
{
return [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use yield instead:

yield ['OK, but incomplete, skipped, or risky tests!', true];
yield ['OK (5 tests, 3 assertions)', true];

{
$this->filesystem = new Filesystem();

$this->workspace = sys_get_temp_dir() . '/infection-test' . \microtime(true) . \random_int(100, 999);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is used elsewhere as well, if that's the case I would suggest to either have a helper for this or an abstract test case for those file-system related tests


public function test_it_can_build_config_with_custom_paths()
{
$originalContent = <<<EOS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'YAML' instead? I think IDEs would be able to pick the syntax highlighting

@BackEndTea
Copy link
Member

Could you run composer cs:fix?

@BackEndTea
Copy link
Member

I'll give this good look over after the weekend, and see if i can break anything.


return new CodeceptionAdapter(
new TestFrameworkExecutableFinder(CodeceptionAdapter::EXECUTABLE),
new CodeceptionInitialConfigBuilder($this->tempDir, $this->projectDir, $codeceptionConfigContent, $this->infectionConfig->getSourceDirs()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just trying this out on a project. A recent MR has changed the variable $this->tempDir to $this->tmpDir. I had to fix this before I could use the PR.

$codeceptionConfigContent = file_get_contents($codeceptionConfigPath);

return new CodeceptionAdapter(
new TestFrameworkExecutableFinder(CodeceptionAdapter::EXECUTABLE),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not Found For Me. Changed It To TestFrameworkFinder and started working again.

 was renamend to TestFrameworkFinder in 955c11e
@Fenikkusu
Copy link
Contributor

So far so good. I'm having issues running it through a docker image ( #254 ), but I think that is related more to infection than the codeception work.

@sidz
Copy link
Member

sidz commented Mar 23, 2018

@Fenikkusu first of all this PR has issues which should be fixed. @tobiasstadler could you please check the latest Travis build https://travis-ci.org/infection/infection/builds/357027746?utm_source=github_status&utm_medium=notification and fix all issues

@Fenikkusu Fenikkusu mentioned this pull request Mar 28, 2018
3 tasks
@Fenikkusu
Copy link
Contributor

I've come across an issue I didn't come across originally. If you are using params:env:.env, The system chokes because it can't find the .env file. I'm working on resolving the issue and have a partial fix at https://github.com/twistersfury/infection:codeception. However, this resulted in additional errors I have not yet had the time to resolve.

@Fenikkusu
Copy link
Contributor

I've figured out the additional errors. I was running into two issues. The first was I had an acceptance suite installed, but no actual tests installed. When coverage ran, codeception choked because it was trying to connect to the acceptance url, which didn't exist. This error was easy enough for me to resolve, just by disabling the suite. However, it brings up the point of should the ability be added as part of the configuration to be able to specify which suites are ran?

The next issue took me a bit to track down. This time, phpunit was choking on tokenization for coverage with phpunit. Basically I had files in the project that were empty, but weren't PHP files. I tracked down the command infection was running and tried to replicate the specifics. I ultimately realized that when the Codeception suite runs, it is setting the configuration of coverage files to '*' based off the infection config. IE:

    "source": {
        "directories": [
            "src"
        ]
    }

Is translated into:

coverage:
  enabled: true
  include:
    - src/*

Because it is '', it is including all files, even non-php ones. I'm fairly certain if we just remove the '' that will fix the issue. My question though, at this point is if there is a specific reason why * was used instead of just using the folder path?

@DavertMik
Copy link

However, it brings up the point of should the ability be added as part of the configuration to be able to specify which suites are ran?

Yes, there is no sense to run mutation testing for acceptance or functional testing. So for those suites inflection should be disabled, that's for sure.

@Fenikkusu
Copy link
Contributor

So for those suites inflection should be disabled, that's for sure.

I agree that they should be disabled, but I still think the ability needs to be passed onto the user via configuration. I say that because the suites are not limited to unit, functional, and acceptance. For example, I've a project that has a suite called 'legacy' and another called 'coverage'. Both of these suites run under the pretense of unit testing and should be ran as part of the testing. I think in the end, an option in the json configuration needs to be added for suites. When not specified, it defaults to 'unit'. When specified, it runs all the suites specified. This could easily be a string that just directly injects into the command line arguments.

@Fenikkusu
Copy link
Contributor

Update: I tested the code using just the path and not the '*'. I still run into the same issue. The easiest solution is to just change it to be *.php. Done and done. The only problem I see with that is that the possibility of file names not ending in .php. IE: php5, php4, and phtml are all considered valid extensions, though rare (with phtml likely not being a class).

@Fenikkusu Fenikkusu mentioned this pull request Apr 5, 2018
@Fenikkusu
Copy link
Contributor

Fenikkusu commented Apr 6, 2018

Noting Additional Issue: If codeception errors (IE: use db module without an accessible db), the error isn't getting properly echoed by infection.

Update: Interestingly it is actually working on my local comp. The CI however isn't outputting anything...

@maks-rafalko
Copy link
Member

maks-rafalko commented Apr 6, 2018

the error isn't getting properly echoed by infection

that sucks. Could you please try to set an error hanlder as it is done in #275 and see if it helps?

@Fenikkusu
Copy link
Contributor

After a bit of digging, I was able to figure out how to get this to replicate consistently. If I'm not debugging, I never get any output from the error (again, use the db module with invalid db). If I put in a break point and force the codecept command to finish before the incept command has time to kick off the wait process, I get the proper output. AKA, break on line 303 of Symfony's Process.php file and step the codecept run to the next line. Let the codecept finish, then continue running infection. In this case, you will get the correct output.

@Fenikkusu
Copy link
Contributor

that sucks. Could you please try to set an error hanlder as it is done in #275 and see if it helps?

@borNfreee, I tried that as well but saw no difference. At this point it appears to be a race condition in the underlying symfony framework.

@BackEndTea
Copy link
Member

@tobiasstadler are you still working on this?

@tobiasstadler
Copy link
Author

Sorry, I am not working on this anymore.

@BackEndTea
Copy link
Member

Sorry, I am not working on this anymore.

I'm gonna go ahead and close this PR then. Thank you for the work you put into it.
Even though this won't be merged it may be usefull if someone wants to pick it up at a later point.

@BackEndTea BackEndTea closed this Aug 9, 2018
@Fenikkusu
Copy link
Contributor

I've already done number of updates to this, though I haven't refreshed it in a couple of months. I'll open a new MR once I have a chance to rebase and clean up. I've been using it in a few applications, so I think it's about 80% there.

@maks-rafalko
Copy link
Member

Implemented in #800

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

Successfully merging this pull request may close these issues.

Integrate Codeception testing framework
8 participants