Skills Upgrade #7

April 17, 2024

Welcome back to “Skills Upgrade” a Talking Drupal mini-series following the journey of a D7 developer learning D10. This is episode 7.

Listen: 

00:00
 

Watch: 

Topics: 

  • Review Chad's goals for the previous week

    • Test Example
    • Set up phpunit.xml
    • Start with FrontPageLinkTest.php
  • Review Chad's questions

    • In the testing_example module, the file "src/Controller/TestingExampleController.php" has a function for simpletestDescription(). Is this an outdated artifact that should have been removed at some point? The module itself doesn't appear to use Simpletest elsewhere and appears to only rely on PHPUnit.
    • What do you recommend for the minimal code structure to include for any given test type? Is the Testing Example module an ideal model or are there other resources I should review? The testing reference from Selwyn was helpful.
    • In the "FrontPageLinkDependenciesTest.php" setUp() function, the createContentType() function is called without specifying the type. Is that set somewhere else? I may have overlooked it. Nevermind—it's set using randomMachineName() in the createContentType() function.
      Is there anything extra or standard to write in tests for drupal.org?
  • Tasks for the upcoming week

    • Smart Date - Martin (maintainer) to review promptly, I've already chatted with him about it. Create a new functional test: "submit a range with an end time before the start and validate that an error is returned"
      • Create an issue in the Smart Date queue and assign to yourself.
      • Create an issue fork.
      • Check out the issue fork locally.
      • Write (and test) the test locally.
      • Commit and push to the issue fork.
      • Mark issue as "Needs review".
      • Ask someone to review - if all looks good, the reviewer will mark as RBTC.

Resources: 

Chad's Drupal 10 Learning Curriclum & Journal
Chad's Drupal 10 Learning Notes

The Linux Foundation is offering a discount of 30% off e-learning courses, certifications and bundles with the code, all uppercase DRUPAL24 and that is good until June 5th
https://training.linuxfoundation.org/certification-catalog/

Transcript: 

 [MUSIC]



 Welcome back to Skills Upgrade, a talking Drupal mini-series following the journey of a Drupal 7 developer learning Drupal 10. This is episode 7. My name isAmyJuneHineline I am a Drupal mentor coordinator and I work at the Linux Foundation as their certification community architect.



 Now remember during this mini-series, we follow a Drupal 7 developer as he upgrades his skills to work on Drupal 10 projects. With that ultimate goal of supporting Drupal 10 project and contributing back to the community. This is a weekly show. Each week a mentor will review the developer's progress, answers questions and set goals for the upcoming week.



 Our developer is Chad Hester. Chad Hester on drupal.org an enterprise technical consultant and solutions architect with a background and website development of over 20 years. And our mentor is MikeAnello Ultimike on drupal.org. He is the co-founder and vice president of Drupal EZ a Drupal training and consulting firm based in central Oregon. I just want to make a note that during the recording for this week Mike was traveling and had internet speed issues, but the audio was prioritized and it's mostly good. So we decided to push forward.



 So let's think back to episode 6. Chad was tasked with looking into PHP unit tests. Mike warns that there's no one place to look for those in the docs. Mike gave Chad a GitHub snippet of a PHP XML file. I wonder how he fared.



 Hey Chad, how's it going? Do you have a good week?



 Yeah, good week. Got through the homework, did a little recreational stuff over the weekend.



 So Chad was this your first foray into automated testing of any kind or have you had, you know, have you done anything like this in the past?



 Yeah, probably more than 10 years ago with Bhat, Gherkin, that sort of thing, but nothing to end up.



 Yeah, I mean that's as far as like what I had you look at was functional tests and that's what you're going to be working on next week. That's a pretty, you know, that's pretty similar to Gherkin and Bhat tests where they are, they're kind of simulating a browser and you have to write code that basically walks, you know, the test through the steps of like load this page, make sure you see this and then enter this value in this field and hit that button. It's all kind of codified, but so just having even a little bit of background and I remember the experience I had with testing was with Selenium in a browser, which I think when I started it was even writing out JavaScript. I think when you recorded a Selenium test,



 I could be mistaken, but I think it was like doing HTML. Like this was a while ago though, so. I think there were like some special plugins.



 Yeah, there were absolutely browser plugins for Selenium to record that stuff. I remember I had, yeah, I did a bunch of a bunch of Selenium tests for big client at the time and we use those a couple of years until they just got updated and they didn't want to update.



 Anyway, let's talk about the examples module.



 I was curious when did drew.org pick up any sort of automated testing? Was it after the switch to GitLab or I know that they had simple tests for a while, but.



 I believe that there were some automated tests going before Drupal 8.



 I did not start writing tests until after Drupal 8 came out. So I don't really have a good handle on that, but I'm pretty sure that there were, there was some automated tests in Drupal 7 and before, but don't hold me to that.



 It's a history lesson if nothing else, but yeah,



 I think the testing example module was fantastic to jump into. And I looked at the items that you had mentioned specifically called out. I got a little hung up on one, but I was able to answer my own question related to it, thankfully.



 Right, I mean a lot of the tests that I want you to look at in the examples module were, actually I asked you specifically just worry about the functional tests. I mean, start there if you wanted to look at more. And because the examples in the testing example module are very straightforward. None of them are very long, but they have the important bits, right? They extend browser test base, which is a Drupal core class, which has kind of all the low-level helper functions or helper methods, I should say, for writing a functional test. They have an example of here's a modules array that you have to define the modules that you want enabled for the test. Here's the theme.



 We need to define the theme to actually be used for this test. And a lot of times you want like the fastest possible theme to make your tests run as fast as possible. So a lot of time, start.



 Setup method.



 So the way these test classes work, functional test classes, actually any PHP unit test class works is any method of the class that either begins with the word test



 or has the at test annotation in the doc block is considered a test.



 And when we run a unit, when we tell PHP unit to hey, run this test class,



 it's basically going to look at all of the methods in that class and only run the methods that either begin with lowercase, TST or have app test in the annotation.



 But for it runs any of those methods, it's going to run the setup method. So the setup method is kind of like this global method where you can put setup work for all of your test methods. So I mean, guess tell me, did that come across in what you saw?



 Yeah, the setup method wasn't in the front page link test, which I'm guessing it just didn't have any setup. So that's probably why it didn't do anything with it. And but the front page link dependencies test did. And I thought that was interesting at the very top of it. It says always call the parent setup. So parent colon colon setup.



 But yeah, it made sense once once that breakdown was put together. Yeah.



 Yeah, I think the best way to learn how to write, forget about functional tests, but any of these, you know, unit kernel functional functional JavaScript tests is to open up an existing test class for either a core module or a well-written contrib module. And I normally will get the lay of the land by looking at, okay, does it in browser test space? If great, if so, then great. If it extends something else, does that something else extend browser test space? And normally, you know, for functional tests, somewhere in the hierarchy will have extended browser test space.



 If there's something in between, you probably want to look at that base class just to see what that's giving you. But so, you know, confirm it's extending browser test space, look at the modules that are enabled in the modules array.



 You could take note of the theme, it normally doesn't matter, but sometimes it does. And then I normally go right to the setup method first.



 Because the setup method is really kind of, that's defining, okay, what's, you know, again, it depends on how the test was written. It's possible to write all the setup code as part of the test method itself. But if you've got a longer test class that has multiple test methods, normally the stuff in the setup method is kind of the information architecture or the configuration of the test site.



 So I like to have a good handle on what's in there. And a lot of times if you're a poor test, either that setup method will be called other helper methods or the test class itself will have extended something other than browser test space.



 Strictly to get all the setup in kind of one place.



 Okay, so I normally do all of that before I look at any test methods.



 It's kind of just to get the lay of the land type of thing. It certainly made a little bit more sense when I took a look at some of the other, I mean, I looked at those first three, but I also looked at a handful of other tests that were in that same folder just to see, well, what are these other things doing?



 And I think, for example, the example functional test was a decent one as well. It had a little bit more in terms of permissions that it was setting up.



 And there was another one that I think was talking about, you know, setting up a content type as well as a user or something like that. Right. Yeah, using those handy little...



 the helper functions provided by browser test space. There's like a whole create user, a Drupal create node, a Drupal login.



 Really, really handy stuff.



 One of them even had an error that popped up when I ran PHP unit about deprecated code with the install schema. So that was good to see that even those sort of deprecated code things come up. So I guess if, you know, we're moving from Drupal 10 to 11, that might be a good resource to start using



 when updating Contrib or one-tone custom stuff. Sounds like a nice contribution opportunity for somebody as well. Yeah, for sure.



 So you did mention, I see in the notes, you mentioned something about a method that you had a question about.



 Yeah, I actually had two, but we'll start with the first one. So in the... That's good, fire away.



 Yeah, in the testing example controller,



 so the controller not so much the test, it had a function for the simple test description.



 And I think I answered my own question as I was reading the documentation because it like they left a little breadcrumb sort of comment saying this is deprecated, it will be removed. I think it was in Drupal 9. And I'm still looking at it. It's still there. So clearly it wasn't removed, but I'm presuming that there may still be artifacts of simple test sort of functions in some of these examples.



 That specific method that you're talking about, simple test description, it's kind of, I don't want to call it, red herring is on the right term. I don't know what to call it, but it's...



 It actually has nothing to do with simple test itself, other than the name of the method and the content that's written in the associated template file.



 Okay. It's kind of silly. I don't know. I mean, honestly, I look through the tests. I didn't see where that method is used as part of a test.



 You know, it's part of the testing module. There's a route that basically calls that method as its controller. And that simple test description method is a method on the controller that gets called by a route and it renders some content to the page. And the content is basically in that, in a template file that says simple test has been deprecated, blah, blah, blah. So it actually doesn't... It's more of like stuff that's about simple test. That's for simple test, if that makes sense. Yeah, I think that makes sense. So that was the one function. It's a non-issue, basically. Yeah, yeah. It was more of a head-scratcher curiosity thing. The other was a little bit more specific about the test and I crossed it out, but I think it's still good for people to be able to see these things. I feel like whenever you're dealing with object-oriented programming, there's a bit of a rabbit hole that you can get into just trying to understand each class, its structure, things like that. A good exercise for sure, but probably not. Probably need to budget some time for those sort of curiosities, if you will. So when I was looking at the front page link dependencies test.php, the setup function has a function call to create content type. So here I am, you know, Drupal site builder developer thinking, well, but you didn't tell it what content type name to use or any other details. Sure enough, the function itself has a random machine name function. So if there's no argument pass for type, it just randomly generates one. So obviously that's practical for just testing where you don't really need to kick, but that stood out a little bit to me as well. The pattern all over as your test.



 When you create a user, the Drupal create user method,



 you pass it an array of permissions you want that user to have.



 Yeah, but if you do any site building, you know that well, you don't actually assign permissions to the user, you sign permissions to a role than the role to a user. And what that method does is it actually creates a role with again, a random machine name, but okay, that's kind of irrelevant to the test because you don't care about that. You just want the user with the right permissions. I was going to move on to the other question, but it sounds like you have something more relevant to this thought right now.



 No, I was going to transition to the next question as well. So go ahead.



 We're anticipating each other.



 So all of these tests, I think have several different classes and several different functions that make sense like the assertion statements, things like that.



 I think the example, the testing example does a good job of saying here's just a few very small things that you can do.



 But separate from that, is there like a minimal code structure that we would typically want to use for any given test type or is the testing example module like the ideal model? Are there other resources that we should review?



 I mean, there's always other resources. I mean, there's... So, you know, I go by kind of the rule. You want the test to be as simple as possible, which is not always possible, right? I like for us to be as approachable as possible, not just for future me, but for other people looking at the code. I know when I write a test, there are probably only five or six different assertions that I use, even though there are probably, if I had to guess, 50 different assertion methods that you could use. Okay.



 So, there's a standard like in a cert where the first argument is a conditional.



 So you're basically saying, you know, it's basically an if statement. You know, if the conditional, then the test passes otherwise, that assertion fails type of thing, and there's a few others like that.



 You know, I would say just, you know, make it possible, but look at other modules, for example, that's, you know, I know I've said this before, you know, earlier today, but I really think that's the best way to write tests is like learn the layout.



 As we discussed earlier with the setup method and the modules array and all that stuff, really get a kind of just at least a 10,000 foot view of what are the helper methods available



 and then start racking open, you know, other tests, you know, core tests. Some are really good to look at for beginners. Some of them will melt your brain.



 So be careful with that. Yeah. Contrib module, kind of the same thing.



 Okay, so I guess it kind of devolves from core might be a little bit more standardized and ideal. Contrib may be a little bit more loose and opinionated by the contributor themselves or maintainer. And then something custom is, well, your team's trying to figure it out as much as you are. So it might not necessarily be the most ideal way, but I think you're striking at something that's a bit like the procedural code side of Drupal before eight.



 A lot of people had the more like script kitty for the kind of old-school term way of you kind of learn by just reading what other people are doing. So I think that's some pretty sage advice here too. Yeah, I think, you know, with most, you know, module development in Drupal, there's so much good code out there to model your code on. It would, you know, it would kind of be doing yourself a disservice if you just try to like reinvent the wheel. Yeah, and it's not just about the documentation. It's about seeing real working machinery to learn. Although I do see in your notes, you did mention Selwyn Pullit. I hope I pronounced his last name right. P-O-L-I-T has a open source Drupal book. I don't know what the right name is there, but he does have a section on testing, which looks like you found helpful. Yeah, one of the first things that actually I think I felt was helpful above and beyond that testing example was he does a good job of breaking down, like you said, some of the assertions, but also, you know, there are different types of tests and when you do those tests, there are base classes that you should consider first. Like a PHP unit test, you would use the unit test case or if you have the functional test, it would be that browser test base that you were talking about. So I think that's it's so direct the documentation that he has. I found that very helpful and the testing example module didn't have a functional JavaScript test. So I think that was kind of interesting too. All right, well, functional JavaScript tests are kind of like their own little beast because there's more dependencies required for that. So for me, I recommend for folks learning automated to start with a functional test. Just because they're the easiest to understand, they're the easiest to get going with. But in all honesty, when I'm working on a custom module or contrib module and I need to write a test, I kind of go the opposite direction. I think, okay, can I do this as a unit test?



 Yeah, these tests are the fastest. They have the least dependencies. They're normally, if you can get away with the unit test, in this case, it'll be unit slash kernel test because they're kind of the same thing just with dependencies. But if you can get away with unit or kernel, your test is going to run faster. And generally, it's going to be more straightforward.



 Well, I don't know if I should, that's not always true, but I kind of preference unit and kernel tests over functional tests when possible.



 I usually thought of unit tests sort of like here's the structure that's intended to exist. Let's make sure that it actually works the way it's supposed to exist with whatever it is that you're testing.



 And I'm seeing functional tests a little bit more aligned with like user stories. Like here's what a user should be capable of doing. Is that your take as well?



 Absolutely. Yep.



 When I'm working clients and I am trying to convince them to add more to include money in the budget or test,



 normally the way I sell it is with a functional test, to be honest.



 Because I can sell saying we can test that your most valuable process on this site works.



 Okay, I think that makes sense, especially me coming from more the UX world these days. Are we ready to talk about next week?



 Yeah, yeah.



 I'm a little bit anxious and excited, but I think this is where the rubber starts to meet the road, right?



 A little bit, yeah. So, you know, think of this as the capstone project, no pressure.



 So I talked to the maintainer of the, I put the wrong module in there, of the smart date module. Okay.



 Martin, who is a contributor to Talk and Drupal, so anybody who listens to Talk and Drupal knows who Martin is.



 And I've talked to him about this as well.



 I'm aware that smart date has some tests, but doesn't have great test coverage.



 Well, let's put it this way, it has good test coverage, but it could be better. So I'm talking about, he suggested several functional tests that you can write. And the one I selected, and I basically copy and pasted it into this document for you, and I'll read it verbatim.



 A functional test to submit a range, a date range, with an end time before the start time.



 And then when that gets submitted, validate that an error is returned.



 So kind of a simple, you know, kind of a obvious error. Nothing that is too, you know, arcane or difficult to kind of understand. I think anyone who just listened to that sentence, it would make sense that, sure, date range and the date, or the date is after the date, that's going to be a problem.



 Okay, so the steps for you. Yeah, I can see the step. I imagine that the error that would be registered would be something that triggers from the controller or the plugin.



 Itself for that input field. So maybe that might tip me off as to what to look for in the test.



 But you were going to say the steps. Well, here's what you're going to have a good question.



 I would spin up a fresh Drupal 10 site, install smart date, and reproduce this error manually. Well, that's your assertion. Your assertion is going to be, you're going to do all of this setup.



 And then you're going to create a node that has a smart date field on it, a smart date range field on it. You're going to put the end time before start time, you know, programmatically hit the submit button and you're going to look to see if that error message appears. All right. That seems like a few enough steps that I'm sure it will take some time to find the right functions. But yeah, that seems quite doable. And I recall listening to the smart date podcast episodes. I'm familiar with that whole set of things from Martin. It's a pretty impressive module.



 Well, you won't be alone on this. So, you know, I'm available. Martin's available.



 So here's kind of the big picture of what we want. First, create an issue in the smart date issue queue. And, you know, describe what you're doing.



 Categorize it properly and most importantly, assign it to yourself.



 That's one of the dropdowns you can assign it. So just make it to yourself.



 Then we're not going to use the patch workflow. We're going to use the merge request workflow. So you're going to create an issue fork.



 And then you're going to follow the actions. It will all be right there on the issue that you create to that issue fork. There's some git manager on.



 And then you write the test locally. Make sure the test works locally. And we'll come back in a minute.



 When you think you're done, you commit and push to that issue fork.



 So then now that changes up in an issue fork on Drupal.org. You go back to the issue queue to your issue. You mark it as needs review.



 Once you're there, you can ask me. You can ask, you know, anyone really Martin. You can ask anyone to review it. It will take that second person or maybe a third person. It might be some iteration going back and forth with you to make some small changes. But the goal is to get that to be marked as RBTC, which is reviewed by the community.



 And once it's RBTC, then we'll ask Martin to merge it in. And you will have a commit to the smart date module.



 Okay. Now, all that being said, the real hard part is to write the test.



 For sure. But I think what you looked at last week, yeah, what you looked at last week with the examples module is a good framework.



 Where you're going to have more work to do is in setup. Because you're going to have to create a content type at a smart date range field.



 Right. Because remember, when you start the test, there's no information architecture. You've got to create that in your setup.



 And then in the test, it's in the test method itself, you basically create a node of your type. Okay. And create an edit array with your two date values in the right format. And then there's a submit form.



 Is it a method? I can't picture right now, but there's a relatively easy way to submit a form with the given edit array.



 And then the very next line, you're just going to check to make sure the error comes back.



 The error is displayed on the screen. There's like a text method or something like that. The example functional test method has a creating a node test. So I think that could be a handy reference. It's checking the title, which might also be good to just verify that the node was created.



 And then I can just add the additional check of is this field what we expect it to be? Right. But before that, you have to have a content type that you can assign a start date field. So you can assign a start date field. So there's lots of examples of that code.



 Might be examples in smart. I don't want to I don't want to put your right to it. Sure. Sure. It's still see if you can find some if not, I am more than happy to help say Mike. I need a hint. So, you know, how to get a hold of me.



 But this is a pretty common task, right? And this is one of those things like again, when I teach this stuff, I don't want to do that. But this is one of those things like again, when I teach this stuff, you know, what I tell my students is like never you're never going to start with like a blank page and start writing your test.



 Right. Use use Drush generate to create the scaffolding and copy and paste. Okay. Other tasks that's created type and slap field on it. You said something earlier too that I kind of want to really honor is you keep the test simple. Here, you know, you're checking to see if there's an error when a certain condition happens. My brain automatically thinks about like the other possible conditions of well, what if the date the start date is earlier or equal to the end date and I'm assuming that would make sense as two completely separate test functions that that would run to keep them isolated. Is that your feeling? I mean, again, that's that's kind of like a judgment call at the time. Okay.



 In a single test method. I mean, you could absolutely create three nodes.



 You know, one node where is before this update another node where they're identical and another one where they're correct. Okay, and then just submit each of them and you know and have one assertion for each that's not there's not a whole lot of worry there as far as like things going sideways. If it was something more complex, yeah, you might want to isolate them, but yeah, you can use your judgment on that one. All right. Yeah, I think that'll I think that'll be helpful and hopefully that helps our coverage here.



 Well, good. Certainly, I think I think it's a good challenge. I think it's straightforward, at least from the assignment steps standpoint and knowing that not only will there be an issue queue issue related to it, which means that technically the whole community can help with it, but that you and Martin would be available if I truly get stuck.



 I think if you get stuck, it'll just be a little bit stuck. It won't be a lot stuck.



 Yeah, so all right have at it and we'll talk in a week. Yep, sounds good. Thank you very much.



 I enjoyed how Mike went through how the functional tests are set up in the examples module explaining that setup method and why we choose simple themes when we do testing. It's sort of a thing I should have already known, but it makes a lot of sense that when we when we have those simpler themes, of course, the tests are going to run faster because there's less to test. Mike also reminds us to start with functional tests when we're diving into this learning opportunity. He thinks that they're easier for us to understand when we're beginning, but



 I appreciate the fact that he explained he actually runs a unit test because they're much faster.



 Chad's homework was to write a functional test for the smart date module and as a triple mentor, I am super thrilled that Mike explained the contribution workflow and what to expect during that whole life cycle of a triple.org issue from creating an issue. Chad assigning it to himself all the way through getting the test merged in and remember.



 The mentor, I like to talk about this a lot. We're not using those patches anymore, but we're moving towards that merge request workflow exciting stuff.



 Skills upgrade is supported by talking Drupal and Drupal easy. You can visit talking drupal.com for this episode show notes and resources and visit drupal easy.com and register for an upcoming intensive Drupal training course. To learn more about or contact Chad, you can find him at Chad K Hester.com and Mike Canello. If you want to contact him, you can visit drupal easy.com slash contact hyphen us and he is ultimate on drupal.org mastodon and all the social networks. You can find me Volkswagen chick on Drupal mastodon and LinkedIn and I work for the Linux Foundation and we're offering that unique opportunity to get a discount of 30% off e-learning courses. Certifications and bundles with the code all uppercase Drupal 24 and that's good through June 5th. Thank you.