Our browser tests now need $defaultTheme
10x: Drupal\Tests\BrowserTestBase::$defaultTheme is required in drupal:9.0.0 when using an install profile that does not set a default theme. See https://www.drupal.org/node/3083055, which includes recommendations on which theme to use.
There is one of these warnings for each of the 10 tests in \Functional\TypedDataFormWidget
Change record https://www.drupal.org/node/3083055
I will work on a patch for this.
| Comment | File | Size | Author |
|---|---|---|---|
| #13 | 3109923-13.interdiff-12-13.txt | 1.5 KB | jonathan1055 |
| #13 | 3109923-13.widget-form-test-base.patch | 9.59 KB | jonathan1055 |
Comments
Comment #2
tr commentedFor this I think we should add an abstract test base which can be used by all the TypedDataFormWidget tests defined by this module and also by any tests of form widget plugins defined in contrib. All form widget plugins need to test the same things, testIsApplicable(), testFormEditing(), and testValidation() at a minimum, so let's do what we can in the base class setup (including defaultTheme) and define these abstract functions to force subclasses to implement these tests. Subclasses can of course add additional tests, but we want to ensure that they all test at least these basics.
Comment #3
jonathan1055 commentedOK, yes. Something like this (only done for TextInputWidgetTest.php in this patch). How were you visualising forcing the actual tests to implement those three standard tests? I have defined them in the base class and set a guaranteed failure assertion.
What's the best way to add
Drupal\Core\TypedData\DataDefinitioninto the base class so that each of the actual test classes do not have to add it? If I add it, it fails phpcs saying 'unused use statement'Comment #4
tr commentedIt's an abstract base class, so just define the functions with no implementations, like this:
abstract public function testFormEditing();This forces subclasses to provide an implementation, but can't force the subclasses to do anything reasonable. However, future widgets and contributed widgets will almost certainly use the the existing ones as a pattern, in which case it will be obvious what is needed in these test functions. Adding a few sentences to the function documentation explaining what they are for wouldn't hurt either ...
Declaring a use in the base class will have no effect on the subclasses. What can be done is maybe define a helper function on the base class. So instead of all the subclasses having to do something like
$this->assertFalse($this->widget->isApplicable(DataDefinition::create('boolean')));, maybe add aprotected function assertIsApplicable($widget, $type)function on the base class (probably also needassertIsNotApplicable()) then the subclasses don't need to deal with DataDefinition at all, they would just substitute that old call for$this->assertIsNotApplicable($this->widget, 'boolean');The whole point here is not to create extra work, it's to gather some common functionality so as to reduce the effort it takes to write tests for new widgets and also to eliminate code duplication so as to have more robust and maintainable code. Whenever I see a "fix" that needs to be made in half a dozen different classes, then notice all those classes are essentially doing identical tasks with very few differences, that makes me think there's an opportunity for improvement.
Comment #5
jonathan1055 commentedOK, thanks for the info. Yes that helper function would be good and I will raise a separate issue for that, which I can work on once this issue is fixed.
Yes I absolutely agree, I always think that too. Reducing code duplication helps us all.
Regarding the abstract test class, phpunit just produces the abstruse and not particularly enlightening message
I do understand the message, but it is not very user-friendly. Implementing those three functions as public, with just a single code line
gives a much more immediately useful message
I have added a
$widgetIdprotected property to each test class, which then allows the creation of the widget to be done in the Base class setUp() insteadThis patch will fail because two widget tests do not implement all of the three mandatory test functions.
Comment #6
jonathan1055 commentedUpdated patch with those two missing testValidation functions implemented. For TextareaWidgetTest I wrote a proper test using a constraint on length, minimum 40 chars. For BrokenWidgetTest there is a just an empty implementation as there is no validation possible (I think?).
Comment #7
tr commentedOK, that is more helpful.
I don't really like this -
$widgetIdis only defined in the subclasses yet it is required/used in the base classsetUp(). And how do the subclasses know they have to define$widgetId? Seems pretty fragile.I suggest instead define the base class
setUp()function to accept$widgetIdas an argument. This base class functionsetUp($widgetId)should callparent::setUp()and also store the$widgetIdas a base class property. Then the subclasses will need to declare their ownsetUp()functions which callparent::setUp($widgetId). This is a pattern also seen in core, for exampleviews/tests/src/Functional/ViewTestBase.php.That sounds reasonable. But doesn't that trigger a 'risky test' warning?
Comment #8
jonathan1055 commentedThanks for the feedback.
I tried that. In the base class
protected function setUp(string $widgetId) {gives:Hence I removed the parameter definition, and extract it via func_get_arg(). If the parameter is not specified the default error message is
func_get_arg(): Argument 0 not passed to functionwhich is not very friendly, so I have made this more helpful by failig the test with:I used the condensed syntax
but let me know if you prefer it written out with a full if .. then .. else
No oddly it seems to be fine. My local testing and on Travis are clean, no warnings.
Comment #9
tr commentedYeah, in order for this to work the new argument must have a default value, so
protected function setUp($widgetId = 'broken')would work, with the id defaulting to 'broken' if it wasn't provided by the subclass. Also, typehinting with 'string' is not allowed in D8.7. See #3038168: [meta] Add scalar type hinting and return value typing.Comment #10
jonathan1055 commentedI can't make it work just with the base class
protected function setUp($widgetId = 'broken'). That produces the similar error but the other way round:i.e. the sub-class definition
protected function setUp()does not match the base class. So now the calling functions have to changed to look likeI tested calling without a parameter so that the widget defaulted to 'broken' but this was very confusing. There was nothing to show what was wrong, just an unexpected set of test failures, e.g. the isApplicable result was wrong. It could be frustrating to try and fix tests when actually you are inadvertantly testing against the wrong widget. So I have defaulted to blank, and force the same helpful test failure message as before.
The problem now is that we get coding standards warnings for each of the sub-class functions:
Patch attached, but clearly this still needs some change.
Comment #11
jonathan1055 commentedI have created #3110650: Move creation of $widget into WidgetBrowserTestBase and postponed it on this issue. The setUp() functions will remain as-is, so that I can get back to scope and fix the $defaultTheme.
Comment #12
jonathan1055 commentedBack to a simple version of the patch, to create the new FormWidgetBrowserTestBase and define $defaultTheme
Comment #13
jonathan1055 commentedI have realised that the
$modulesproperty should be 'protected' not 'public' so might as well make that correction now. Also the sub-classes only have to define additional modules that are extra to those set in the base class.Comment #15
tr commentedTook me a while, but I finally had a chance to look at this again.
Committed. Thanks.
Comment #16
jonathan1055 commentedThank you. Unassigning.
Comment #18
jonathan1055 commentedExpanded title as this issue creates
tests/src/Functional/TypedDataFormWidget/FormWidgetBrowserTestBase.phpAlso I have unpostponed #3110650: Move creation of $widget into WidgetBrowserTestBase