During working on #2350507: \Drupal\Core\Url has no __toString() magic method we realized that the test methods of \Drupal\Tests\Core\UrlTest interfere with each other, which makes reliable tests impossible. Because the \Drupal\Tests\Core\UnroutedUrlTest uses a similar approach, I guess there will occure similar problems. So I took a deeper look into the \Drupal\Tests\Core\UrlTest test class and realized that there are some points that may be solved much better. Here is a list of what I found:

  1. Demo values are created in the setUp method and saved in a property ($this->map). This should be refactored to use the @dataProvider approach PHPUnit offers.
  2. The testUrlFromRequest() should use the @dataProvider approach as well to shorten its code and it should not be necessary for it to be a dependency for several other test methods only because it creates the URL objects (by using @dataProvider this is handled in another way)
  3. The testToArray() method says it covers the Url::toArray(), but does not use it in its test implementation

The \Drupal\Tests\Core\UnroutedUrlTest test class has similar issues like stated in 1. and 2. (here for method testFromUri), so this should also be reworked as best as possible.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hctom’s picture

I will take a look at this and provide a patch asap!

hctom’s picture

FileSize
15.65 KB

Here is the first patch which runs perfectly... except with the \Drupal\Tests\Core\UrlTest::testToString() method... so I am not really sure if this is my refactoring or the method Url::toString() method itself?!

Perhaps somebody else can have a look?

hctom’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 2: 2358709-2.patch, failed testing.

hctom’s picture

Did anyone have a look at my patch in comment #2 and any idea, what might cause the issues?

Fabianx’s picture

I think the toString() will make a full URL, which is with http://etc. so it is the re-factoring that breaks it or the test was incorrect in the first place.

The data provider changed, because the original depended upon urls came from testUrlFromRequest, which are urls created by a path.

Those should still be fully qualified, so I think there are more bugs here ...

hctom’s picture

Hm... I don't think that it has something to do with absolute URLs, because it throws an exception... and I tried to mimic everything just the way it was in the old test - except from the fact, that it uses @dataProvider for its URL object data.

Mile23’s picture

FileSize
2.54 MB

Here's a re-roll.

And a first step you might take:

+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -373,13 +308,38 @@ protected function getMockAccessManager($access, $account = NULL) {
+  public function urlProvider() {
+    $data = [
+      ['view.frontpage.page_1', [], [], '/node'],
+      ['node_view', ['node' => '1'], [], '/node/1'],
+      ['node_edit', ['node' => '2'], [], '/node/2/edit'],
+    ];
+
+    // Create URL objects from data.
+    foreach ($data as &$value) {
+      array_unshift($value, Url::fromRoute($value[0], $value[1], $value[2]));
+    }
+
+    return $data;
   }

The problem here is that we are testing methods of the Url class, including fromRoute(). So if we create a Url object using fromRoute(), then we don't have any isolation of the method from other implementations.

Instead, we should generate a PHPUnit mocked object, using getMockBuilder(). Then you can specify what individual methods return and so forth, and keep everything isolated.

You can do this with, for instance, isExternal(). It's a non-static method which accesses a protected property of an Url instance. So you mock Url, then you use reflection (\ReflectionProperty) to set ::external to a known value. Then you can assert whether $mock->isExternal() returns that known value. For this kind of test you don't really need a dataprovider.

Unit testing is really code analysis, where you provide all the dependencies.

Mile23’s picture

Status: Needs work » Needs review
joelpittet’s picture

Title: Refactor/rebuild \Drupal\Tests\Core\UnroutedUrlTest and \Drupal\Tests\Core\UrlTest classes » Refactor UnroutedUrlTest and UrlTest tests

Retitling.

joelpittet’s picture

@Mile23 want to try that re-roll again? 2.5MB doesn't look re-rollish, your branch may not have been up-to-date with the latest 8.0.x

Mile23’s picture

Hmm. That is a bit large. :-) Will check it out when I can.

Status: Needs review » Needs work

The last submitted patch, 8: 2358709_8.patch, failed testing.

Mile23’s picture

Status: Needs work » Needs review
FileSize
15.72 KB

Re-roll of #2. Ignore patch from #8, but hopefully not advice. :-)

joelpittet’s picture

@Mile23 thanks for the re-roll. Is the suggestion you made about using a getMockBuilder still needed for this patch? It's green and there were no changes/fixes in the re-roll.

Maybe it's good to go as-is?

@hctom what are your thoughts on the getMockBuilder suggestion in #8?

Fabianx’s picture

Generally a mock builder is a good thing to have, but we are not as strict in core about unit vs. integration testing right now.

So we have several unit tests that are really integration tests.

Though typically those tests using data providers / Fixtures are often the integration tests.

Fascinating that tests now pass ...

joelpittet queued 14: 2358709_14.patch for re-testing.

joelpittet’s picture

Do you feel lucky testbot?... well do ya?

hussainweb’s picture

@hctom, sorry. I was on vacation and got busy with work when I got back. I will look into this tomorrow to see if my original issues from #2350507: \Drupal\Core\Url has no __toString() magic method are still relevant. Thanks!

Mile23’s picture

The stuff about mocking really comes down to the question: Is it a unit test if it's not isolated? In Drupal-land it apparently is.

If someone wants to RTBC I won't complain. :-)

Fabianx’s picture

Status: Needs review » Reviewed & tested by the community

This is an integration test, but as Drupal is very used to integration tests and it was an integration test before, I am okay this being kept an integration test that uses a data provider.

Therefore: RTBC

Fabianx’s picture

Category: Task » Bug report

Re-classifying as a bug report due to:

... we realized that the test methods of \Drupal\Tests\Core\UrlTest interfere with each other, which makes reliable tests impossible.

Which is a bug as our tests should be reliable.

dawehner’s picture

Component: base system » routing system

+1

+++ b/core/tests/Drupal/Tests/Core/UnroutedUrlTest.php
@@ -117,105 +109,119 @@ public function testFromUriWithNonScheme() {
-   * @depends testFromUri
+   * @dataProvider urlProvider

While @depends is certainly nice for some usecases like moving state from one test to the next one, its not a good idea for sharing test data. @dataProvider is also much easier to understand.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  /**
   * Tests the __toString() method.
   *
   * @param \Drupal\Core\Url[] $urls
   *   An array of Url objects.
   *
   * @depends testUrlFromRequest
   *
   * @covers ::__toString
   */
  public function testMagicToString($urls) {
    foreach ($urls as $index => $url) {
      $url->setUrlGenerator(\Drupal::urlGenerator());
      $path = array_pop($this->map[$index]);
      $this->assertSame($path, (string) $url);
    }
  }

This needs to be updated... from \Drupal\Tests\Core\UrlTest

hctom’s picture

And part from that, the UnroutedUrlTest should contain this method, too which results in also creating the __toString() method in the UnroutedUrl class, too. This was my point when I tried to convince people in #2350507: \Drupal\Core\Url has no __toString() magic method to fix this issue here first ;)

I will provide a new patch as soon as possible...

hctom’s picture

Status: Needs work » Needs review
FileSize
16.52 KB
1.53 KB

Attached is the new patch containing the refactored UrlTest::testMagickToString() method. I also added this method to the UnroutedUrlTest class, because it was completely missing there. You may review all changes in the attached interdiff as well.

Apart from that: I somehow can't run these tests. Neither from the UI nor from the command line. The latter one always throws an exception that the test classes are not found. Can anyone else check, if these tests are run at all?

hctom’s picture

I just checked the test logs and there is also no occurence of Drupal\Tests\Core\UrlTest and the Drupal\Tests\Core\UnroutedUrlTest only has 2 passes listed, which must be more, because there are more test methods included.

Does anyone know what is wrong there?

Mile23’s picture

FileSize
16.59 KB

Reroll of #26.

Mile23’s picture

Status: Needs review » Needs work

You can run individual PHPUnit tests like this:

$ cd core
$ ./vendor/bin/phpunit --filter=YourTestClassHere

In UnroutedUrlTest, setUp() must be public.

When I set them public I get a bunch of errors like this:

[04-Dec-2014 21:37:05 UTC] PHP Warning:  parse_url() expects parameter 1 to be string, array given in /Users/paulmitchum/projects/drupal8/core/lib/Drupal/Core/Url.php on line 228

[04-Dec-2014 21:37:05 UTC] PHP Warning:  htmlspecialchars() expects parameter 1 to be string, array given in /Users/paulmitchum/projects/drupal8/core/lib/Drupal/Component/Utility/String.php on line 36

UrlTest has the same problem with setUp() not being public and throwing the same errors. It also gives me this:

$ ./vendor/bin/phpunit --filter=UrlTest
PHPUnit 4.1.4 by Sebastian Bergmann.

Configuration read from /Users/paulmitchum/projects/drupal8/core/phpunit.xml.dist

..........................EFFFEEE..........................

Time: 5.52 seconds, Memory: 105.25Mb

There were 4 errors:

1) Drupal\Tests\Core\UrlTest::testGetInternalPath
Invalid argument supplied for foreach()

/Users/paulmitchum/projects/drupal8/core/tests/Drupal/Tests/Core/UrlTest.php:198
hctom’s picture

Do you have any idea why the setup() method has to be public? When I look at other tests in core 90% have protected setUp() methods. Only a small list of tests has public setUp() methods.

But I will have a look at all the other problems.

hctom’s picture

Status: Needs work » Needs review
FileSize
17.62 KB
1.77 KB

@Mile23: Unfortunately you did not really applied all my changes in the reroll of the patch. I fixed this in the attached patch. Also, there was an error, I made in the urlProvider() method of the UnroutedUrlTest class, which caused all the PHP array to string conversion errors, but this is fixed now. I did not change any of the setUp() methods to public. Attached is also an interdiff of all my changes to your patch.

I just reworked the patch and I still have issues that the __toString() method of the URL class does not always return a string (but this should obviously happen when looking through the code). I am not sure, but IMHO there is a bug in the URL class or in the way the toString() method (which is called in __toString()) creates the URL strings.

Mile23’s picture

Do you have any idea why the setup() method has to be public? When I look at other tests in core 90% have protected setUp() methods. Only a small list of tests has public setUp() methods.

Every PHPUnit-based test as a public setUp() method. It's like that because the test runner has to be able to run setUp().

@Mile23: Unfortunately you did not really applied all my changes in the reroll of the patch.

It was a straight reroll.

hctom’s picture

I didnt want to offend you, but as I said: 90% of the core test classes and PHPUnit_Framework_TestCase itself have a protected setUp() method. And of course you wanted to preserve the changes from the upstream version, but my patch rewrites the test classes completely and the reroll brings back the old behaviour with @depends testUrlFromRequest instead of @dataProvider urlProvider. So I changed that back again... I am not that familiar with mocking etc. but when I study the testGetInternalPath() method after the reroll, this can't really be what it should be... Just have a look at the duplicate $url->getInternalPath(); line at the end of the method.

So if the toString()/__toString() errors have anything in common with this mocking stuff, I'd appreciate if somebody might have a look at my rewrite of the UrlTest class (UnroutedUrlTest should be fine right now) and check where mocking might be needed.

And of course the UrlTest class is not found in the test results with the patch above, because somehow Drupal's test system ignores tests completely, if there are Exceptions thrown (which in my case happens with Method Drupal\Core\Url::__toString() must return a string value in /www/8.x-git/drupal/core/vendor/phpunit/phpunit/src/Framework/TestCase.php).

Mile23’s picture

Applied patch in #31.

Ran UrlTest:

$ ./vendor/bin/phpunit --filter=UrlTest
PHPUnit 4.1.4 by Sebastian Bergmann.

Configuration read from /Users/paulmitchum/projects/drupal8/core/phpunit.xml.dist

.................................................FFFEEE.......... 65 / 81 ( 80%)
................

Time: 5.81 seconds, Memory: 105.25Mb

There were 3 errors:
$

Which is to say error reporting crashed, with this error:
PHP Catchable fatal error: Method Drupal\Core\Url::__toString() must return a string value in [..]core/vendor/phpunit/phpunit/src/Framework/TestCase.php on line 1707

This leads me to believe that PHPUnit is having trouble presenting the data set, trying to show you what it is, but failing because _toString() doesn't work for some reason. That reason is likely that its Url object has fallen out of scope, or is set NULL for testing. This might be a bug in PHPUnit, but I doubt it.

Changing Url so it doesn't have a _toString() method any more (and commenting out testMagicToString()), I get some useful results:

There were 3 failures:

1) Drupal\Tests\Core\UrlTest::testToString with data set #0 (Drupal\Core\Url, 'view.frontpage.page_1', array(), array(), '/node')
Failed asserting that null is identical to '/node'.

/Users/paulmitchum/projects/drupal8/core/tests/Drupal/Tests/Core/UrlTest.php:201

2) Drupal\Tests\Core\UrlTest::testToString with data set #1 (Drupal\Core\Url, 'node_view', array('1'), array(), '/node/1')
Failed asserting that null is identical to '/node/1'.

/Users/paulmitchum/projects/drupal8/core/tests/Drupal/Tests/Core/UrlTest.php:201

3) Drupal\Tests\Core\UrlTest::testToString with data set #2 (Drupal\Core\Url, 'node_edit', array('2'), array(), '/node/2/edit')
Failed asserting that null is identical to '/node/2/edit'.

/Users/paulmitchum/projects/drupal8/core/tests/Drupal/Tests/Core/UrlTest.php:201

So maybe those are some clues.

daffie’s picture

Status: Needs review » Needs work

This is a PHPUnitTest. And in this case that means that we are testing the class \Drupal\Core\Url. And that is the only class that should be tested. No other classes. That means that mocks are needed. Lets start with Url::fromUri in the function providerUrl().

tim.plunkett’s picture

Issue tags: +rc eligible

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

quietone’s picture

Title: Refactor UnroutedUrlTest and UrlTest tests » Refactor UrlTest tests to use a dataProvider
Version: 8.9.x-dev » 9.3.x-dev
Category: Bug report » Task
Issue tags: -rc eligible +Bug Smash Initiative

Much of the patch here is outdated. All the changes to UnroutedUrlTest.php have been done. What remains is to refactor UrlTest.php, which on a brief look just looks like it is being converted to use a dataprovider. I don't see anything here that makes this a bug so changing to a Task and updating the title.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.