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

Increasing Developer Experience, no longer extend the TestCase #2419

Closed
linaori opened this issue Jan 3, 2017 · 6 comments
Closed

Increasing Developer Experience, no longer extend the TestCase #2419

linaori opened this issue Jan 3, 2017 · 6 comments

Comments

@linaori
Copy link

linaori commented Jan 3, 2017

The last month or so, I've been working on setting up a tool to test a bunch of applications. Those applications are not in the tool itself and I mainly need to use Selenium and a few helper classes. However, I'm hitting the issue that I can't really do proper DI and I end up having a bunch of traits and "hacks" to get things working.

I would like to increase the Developer Experience when writing test cases, not only when in need of dependencies, but in general. When it comes to using the test runner of phpunit, you're fairly limited to what you can actually do. I would like to propose a few small changes that should be fully backwards compatible with the current system. I have a bunch of ideas besides of this, but if you like the idea of increasing developer experience, I will work those out with more details and post them in a new issue.

My current suggestion:

  • No more need to extend the TestCase without creating a lot of duplicate code
  • The ability to initialize test cases the way you want to (like using a DIC)
  • Composition over Inheritance
  • Moving the test state/context out of the test class
  • No more problems with test functionality and multiple inheritance/traits
/** More of a marker interface to allow DI but still see it as a test */
interface Test {}

/** Kinda like the current TestCase class */
interface TestContext
{
    public function assertEquals($expected, $value, string $message = '');
    public function assertSame($expected, $value, string $message = '');

    // ...

    public function createMock(): MockObject;
    public function prophesize(): Prophecy;

    // ...

    /** @throws TestFailedException */
    public function markAsFailed();

    /** @throws TestIncompleteException */
    public function markAsIncomplete();

    /** @throws TestIncompleteSkipped */
    public function markAsSkipped();

    // ...
}

/** Full support for the current annotations: @group Foo */
final class SomeTest implements Test
{
    private $webDriver;

    /** Not having to extend the TestCase allows DI */
    public function __construct(WebDriver $webDriver)
    {
        $this->webDriver = $webDriver;
    }

    public function test(TestContext $context)
    {

    }
}

/** Just as always */
final class SimpleTest implements Test
{
    /** @dataProvider fooData */
    public function testFoo(TestContext $context, $expected, $actual)
    {
        // or perhaps name it $test, because strlen('$test') === strlen('$this') ?
        $context->assertEquals($expected, (new Simple())->doSomething($actual));
    }

    public static function fooData()
    {
        yield ['expected1', 'actual2'];
        yield ['expected2', 'actual2'];

        // ...
    }
}

/** Don't need this in the test, just around it */
interface TestMetadata
{
    public function getClassName(): string;
    public function getMethodName(): string;
    public function getGroups(): array;
    public function getDependencies(): array;
    public function getDataProvider(): callable;
}

/** So you can create your own test factory */
interface TestFactory
{
    public function createTest(TestMetadata $metadata): Test;
}

/** Perhaps the locator is actually a Symfony container here... */
final class ContainerAwareTestFactory implements TestFactory
{
    // ...
    /** Probably need a resolver to determine how to choose this factory over the default */
    public function createTest(TestMetadata $metadata): Test
    {
        return $this->someLocator->ByFqcn($metadata->getClassName());
    }
}

/** Perhaps a BC layer or how it will function in the end */
final class SomeTestContext implements TestContext
{
    private $testCase;

    public function __construct(\PHPUnit_Framework_TestCase $testCase)
    {
        $this->testCase = $testCase;
    }

    public function assertEquals($expected, $value, string $message = '')
    {
        $this->testCase->assertEquals($expected, $value, $message);
    }

    public function assertSame($expected, $value, string $message = '')
    {
        $this->testCase->assertSame($expected, $value, $message);
    }

    // ...
}
@sebastianbergmann
Copy link
Owner

I have similar ideas but this won't happen before PHPUnit 7.

@linaori
Copy link
Author

linaori commented Jan 3, 2017

Is there perhaps something that can be worked on? It's not something I need right now, but I think it would be a great addition in the future

@sstok
Copy link

sstok commented Jan 26, 2017

FYI, assertEquals is actually static method.
https://github.com/sebastianbergmann/phpunit/blob/5.7/src/Framework/Assert.php#L503 This class content could be moved a Trait so anyone can load and create a custom implementation of https://github.com/sebastianbergmann/phpunit/blob/5.7/src/Framework/TestCase.php

And registering you own TestClass with DI in a suite is already possible https://github.com/sebastianbergmann/phpunit/blob/5.7/src/Framework/TestSuite.php#L203 it's only a bit more work.

@linaori
Copy link
Author

linaori commented Jan 26, 2017

preferably I even use Assert::equals(), Assert::same() etc. I added the example as mentioned above because I once mentioned that the docs still call it $this-> while it's static and it would be kept that way.

@DavertMik
Copy link

DavertMik commented Feb 5, 2017

@iltar FYI we have a similar implementation in Codeception to make the most lightweight PHPUnit test. We ended with this class https://github.com/Codeception/Codeception/blob/2.2/src/Codeception/Test/Test.php
It can be run with PHPUnit 4.8, 5+ (using Codeception as runner).

However, it would be very nice to see decomposed PHPUnit\Framework\TestCase classes in next versions of PHPUnit as well, so everyone could use the exact set of testing features they need and not to take everything.

@sebastianbergmann
Copy link
Owner

I did not have time to do this for PHPUnit 7. I will close this ticket, to me this is related to #10.

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

4 participants