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:
- 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. - 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) - The
testToArray()
method says it covers theUrl::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.
Comment | File | Size | Author |
---|---|---|---|
#31 | interdiff-2358709-28-31.txt | 1.77 KB | hctom |
#31 | refactor-2358709-31.patch | 17.62 KB | hctom |
#28 | 2358709_28_reroll_26.patch | 16.59 KB | Mile23 |
#14 | 2358709_14.patch | 15.72 KB | Mile23 |
Comments
Comment #1
hctomI will take a look at this and provide a patch asap!
Comment #2
hctomHere 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 methodUrl::toString()
method itself?!Perhaps somebody else can have a look?
Comment #3
hctomComment #5
hctomDid anyone have a look at my patch in comment #2 and any idea, what might cause the issues?
Comment #6
Fabianx CreditAttribution: Fabianx commentedI 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 ...
Comment #7
hctomHm... 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.
Comment #8
Mile23Here's a re-roll.
And a first step you might take:
The problem here is that we are testing methods of the
Url
class, includingfromRoute()
. So if we create aUrl
object usingfromRoute()
, 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.
Comment #9
Mile23Comment #10
joelpittetRetitling.
Comment #11
joelpittet@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
Comment #12
Mile23Hmm. That is a bit large. :-) Will check it out when I can.
Comment #14
Mile23Re-roll of #2. Ignore patch from #8, but hopefully not advice. :-)
Comment #15
joelpittet@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?
Comment #16
Fabianx CreditAttribution: Fabianx commentedGenerally 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 ...
Comment #18
joelpittetDo you feel lucky testbot?... well do ya?
Comment #19
hussainweb@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!
Comment #20
Mile23The 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. :-)
Comment #21
Fabianx CreditAttribution: Fabianx commentedThis 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
Comment #22
Fabianx CreditAttribution: Fabianx commentedRe-classifying as a bug report due to:
Which is a bug as our tests should be reliable.
Comment #23
dawehner+1
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.
Comment #24
alexpottThis needs to be updated... from
\Drupal\Tests\Core\UrlTest
Comment #25
hctomAnd 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...
Comment #26
hctomAttached is the new patch containing the refactored
UrlTest::testMagickToString()
method. I also added this method to theUnroutedUrlTest
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?
Comment #27
hctomI just checked the test logs and there is also no occurence of
Drupal\Tests\Core\UrlTest
and theDrupal\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?
Comment #28
Mile23Reroll of #26.
Comment #29
Mile23You can run individual PHPUnit tests like this:
In
UnroutedUrlTest
,setUp()
must bepublic
.When I set them public I get a bunch of errors like this:
UrlTest has the same problem with setUp() not being public and throwing the same errors. It also gives me this:
Comment #30
hctomDo 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.
Comment #31
hctom@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.
Comment #32
Mile23Every PHPUnit-based test as a public
setUp()
method. It's like that because the test runner has to be able to runsetUp()
.It was a straight reroll.
Comment #33
hctomI didnt want to offend you, but as I said: 90% of the core test classes and
PHPUnit_Framework_TestCase
itself have a protectedsetUp()
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 thetestGetInternalPath()
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 theUrlTest
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 withMethod Drupal\Core\Url::__toString() must return a string value in /www/8.x-git/drupal/core/vendor/phpunit/phpunit/src/Framework/TestCase.php
).Comment #34
Mile23Applied patch in #31.
Ran UrlTest:
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 outtestMagicToString()
), I get some useful results:So maybe those are some clues.
Comment #35
daffie CreditAttribution: daffie commentedThis 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().
Comment #36
tim.plunkettComment #45
quietone CreditAttribution: quietone as a volunteer commentedMuch 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.