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
Conversation
Any updates? |
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 :) |
There was a problem hiding this 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 intests/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" |
There was a problem hiding this comment.
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(.*?)#' |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 ArgumentAndOptionBuilder
s (initial/mutation) with different parameters.
But see below suggestions about avoiding changes in this interface
There was a problem hiding this comment.
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() . '"'; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- it will not work since
codeception/codeception
is inrequire-dev
section and all files from this section are absent in PHAR distribution - 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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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
src/TestFramework/Factory.php
Outdated
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]) |
There was a problem hiding this comment.
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?
src/Command/InfectionCommand.php
Outdated
@@ -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)', |
There was a problem hiding this comment.
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 ]] |
There was a problem hiding this comment.
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" ]
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this 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" ] |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this 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) |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 [ |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
Could you run |
I'll give this good look over after the weekend, and see if i can break anything. |
src/TestFramework/Factory.php
Outdated
|
||
return new CodeceptionAdapter( | ||
new TestFrameworkExecutableFinder(CodeceptionAdapter::EXECUTABLE), | ||
new CodeceptionInitialConfigBuilder($this->tempDir, $this->projectDir, $codeceptionConfigContent, $this->infectionConfig->getSourceDirs()), |
There was a problem hiding this comment.
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.
src/TestFramework/Factory.php
Outdated
$codeceptionConfigContent = file_get_contents($codeceptionConfigPath); | ||
|
||
return new CodeceptionAdapter( | ||
new TestFrameworkExecutableFinder(CodeceptionAdapter::EXECUTABLE), |
There was a problem hiding this comment.
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
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. |
@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 |
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. |
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? |
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. |
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. |
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). |
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... |
that sucks. Could you please try to set an error hanlder as it is done in #275 and see if it helps? |
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 |
@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. |
@tobiasstadler are you still working on this? |
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. |
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. |
Implemented in #800 |
Fixes #45