 Okay, I think we're gonna get going here. Thank you for staying and coming to a five o'clock session, especially one that's going to be code heavy. So this is a talk by me and Cat Bailey, but you'll notice Cat Bailey is not here. So she couldn't make it, so it's just me. I'm Mark Sandoval. I'm a performance engineer at Acquia. Although my job is mostly performance, I also do a lot of work just to object-oriented programming in general and Drupal 8, and I helped get PHP unit into Drupal 8. So I'm passionate about unit testing and unit testability. So first, what is a unit test? It verifies the behavior of a unit of code in isolation, independent of application context. They're fast, but fast tests aren't always unit tests. Unfortunately, they do not replace integration and acceptance tests. As slow as our test suite is now, we will never get it into like 10 seconds by making them all unit tests because we can't convert them all. We still need acceptance tests and integration. So why unit test? Mostly code quality. When you force them to do unit tested, you're forcing that code to be context-independent. You're saying I can run this code and I can verify its behavior outside of the context that it's run in. A good unit test also serves as documentation. There are other communities that are not necessarily in the PHP or especially Drupal world where the tests are actually their best documentation and oftentimes they're better than written documentation because you can see exactly how the thing's used. The refactoring, it's very, very painful to refactor without unit tests. Anyone who has worked on Drupal 8 and had a random test failure for some web test and had to debug that, you've probably felt that pain. The feedback loop on that is very, very slow and it may break something that is completely unrelated to what you're doing. The feedback is also extremely valuable. When you're coding and you have unit tests, you're constantly running them and you know instantly when you break something and it's a constant feedback loop with the tests. Now, we talk a lot about testable code. I'm sure if you've been involved in Drupal 8, especially if you're familiar with the service container and the general dependency injection trend in Drupal 8, you've probably heard about testable code. But just to define it, I like this quote from the growing object-oriented software guided by Testbook. For a class to be easy to unit test, the class must have explicit dependencies that can be easily substituted and clear responsibilities that can be easily invoked and verified. So to illustrate that point, and here's an example of some code from D7. This code adds a role to a user. I believe it is triggered with the action that does that. It is an action. Right, it's just really confusing because the word action never appears anywhere here. So if you look at this code, if we look at the dependencies, we see the parameters. So you have an explicit dependency on accounts, operation and role. You have operation because it does multiple things. And then you have implicit dependencies. You have DB query, user load multiple, and user save. So those right there. And now if you were in a unit test and you were trying to run this, you would need all of those functions available. So you'd include the files that have those. But those functions call other functions. And so now you need all the files that those are in. And the files that those are in. And on and on. This is the level which I stopped doing this by hand. So the same code from D8. It's an add role user object, a little bit strangely named, but it is an action plugin. It takes the account, and then it has the role. The role actually got injected via its constructor because it's a plugin, that's a little weird. But two explicit dependencies. And here, all we have is a call to has role. And then if it doesn't have the role, it calls add role and save on that user object. So no function calls. Now if we look at the test for this, this test actually exists in core. You start that out by creating an account mock. And in this example, it's using mock builder. Sometimes you need that when you're creating a mock from a test or from a class instead of an interface. I'll show that later. And then you set up an expectation. You're saying that the method add role will never be called. And then you set up an expectation that the has role, the has role method will be called with this value and it will return true. And so that's basically saying, and then if you notice the test is called test execute add existing role, it's saying I'm gonna test the situation where the user already has this role. I'm gonna stub the has role methods that it returns true. And so that I'm gonna verify that add role never gets called. And then you basically set up that object and then you just do execute. You'll notice there's no assertion at the end. Who here's written a simple test for? PHP unit? So especially the people who have done simple tests, you're probably used to just always using like this assert equal assert something. The assertion here is in the expectation. This is an example of using a true mock. And when you say like on the second one, that's more of a stub because it's saying this any, but when you say this never, that means if the method is called once, the test fails. The test only passes when it gets called zero times. So I would say in this example, we have clear responsibilities and they're very easy to invoke and verify. And they're explicit and it was very easy to substitute them. So I mentioned briefly mocks and stubs to clarify these terms. They're two types of a test doubles. It's a sort of a recentish term, but it's a useful one for describing these objects. So say we have this class. It's a super simple class. It takes a storage interface. The only method it has is get stuff and then it just calls it delegates directly to stuff storage get. And then you just create the interface that just has get and set extremely simple. So the first type of object and the simplest is a fake. So when you're using a fake, you actually just implement that interface. You just create a class. And in this example, it's basically in memory storage. If you worked on Drupal test before, you'll see this a lot. This is what we've done quite often. We did that in D7. And then when you test it, you just instantiate that object. You set it that value manually and then you inject it and assert the response that comes back and make sure that you got the right thing. Now a stub is similar. Except with a stub, you don't actually have to make that class. As long as the interface exists, you can just tell PHP unit getMock for this interface and then set an expectation. And then expect any method get and it'll return that value and then use the same thing as before and then assert the value at the end. So you're asserting the state that came back. I think when people see this initially, it maybe seems like a lot of setup and it may be a little laborious. But I think compared to actually creating a class in the other one, this is still less work. And it's probably a little confusing also that the method is called getMock and I'm saying that this is a stub. It's not that great of an area, but if you actually look at the PHP unit documentation, they're clear about these are stubs. They just all come from the mock method because they can sort of be both. So a stub is used for indirect input and it asserts on state. Meaning you use a stub when you have input that's happening or input that you need to do the job that your method needs to do that is not passed in the method signature. And then you do an assertion on state. So mocks on the other hand are a bit different. So you might say, you thought we've always used mocks in Drupal. If you looked at the file system, you'd see a lot of things called mocks. Those aren't actually mocks. Pretty much all of them are fakes. So here's what the test with a mock would look like. Notice it is very similar to the stub, but the big difference is instead of saying this any, I say this once. And there's my expectation again, if it gets called twice or if it gets called zero times this test fails. And then I don't actually, like I would assign that stuff variable and I don't actually do anything with it because I don't care what the return value is because all my classes responsible for is delegating this call to another class. I don't necessarily need to return, I don't need to test the return keyword. So in this example, I know all I need to test is how this object interacts with the other object. So mocks are used for indirect output and they assert on behavior. So like I was saying before, we're basically asserting that this object acts or it behaves how it should with this other object. They send the right messages to each other. Now, if you've looked into mocks, you've probably heard somebody say that you should only mock what you own. It's a bit of an advanced topic for us, but I think it's worth introducing. And so from similar people, but in a different paper, mock objects is a design technique. So programmers should only write mocks for the types that they can change. Otherwise, they cannot change the design to respond to requirements that arise from the process. Meaning when you're programming with mocks and you're writing your tests first, and then you realize, okay, at this point, I have this other interface that I haven't made yet, but I'm making it now so that my tests can finish and so that my class can do what it needs to do. At that point, you're looking at the design and you might make changes to it and you can't do that with an object that's not your own. If that doesn't make sense, that's totally fine. It's not a hard rule. You can mock other things, but in general, like a simpler example is don't try to mock PDO. That's not a good idea. Instead, create a class that wraps PDO and then mock your class that you just made. Now you're mocking something that you own. So look a bit about, or look a bit at Drupal 8 and how this helps us. Here's an example cap put together. This piece of code sort of reformatted to fit on the slide and cut down from the subpath auto module. All it does here is just a lookup on path to get the alias if it's an alias and then cuts it down. The important part is the Drupal lookup path. So in this example, like the one before, you end up with those dependencies. I did not trace them farther than that. They have more dependencies, but I didn't wanna do that again. But I mean the big one there, DB query. It's sort of game over at that point. So here's the new version. When you turn this into a plugin. Path processors are an object, like they're all objects in Drupal 8 and they all get run in a certain sequence. And so this actually had, this object gets, actually let's skip to that. It defines a service for itself and it says I need injected the path processor alias. And so there's another path processor object that all it does is it resolves these aliases and I need that injected into my object. And so once that's injected, all I do is I call this process inbound on it. And then you have to tag it path processor inbound. That's a little goofy. So here's the test for it. Basically do the same thing you do before because this is a class and not an interface. Use the mock builder and disable the constructor because you don't want the code from the constructor running. And then you set an expectation that process inbound is gonna get called once with that argument. And then you're giving it a return value. And so you're basically simulating it, giving you the unalias version. And then pass that mock into, it's sort of a mock and a stub in this sense. If I had this any, this would just purely be a stub but it doesn't hurt to just say this once and get both of them. So you inject that and then go ahead and call process inbound and then assert that you get the right value. And so this is a very unit testable version and really that's just not much trickery there. That's just how this thing works in droop life. So you might say testable classes all need to be defined in services. I've definitely read this in IRC more than a couple of times. That's not true. So as an example, it's kind of a silly example because this is hooks but it's a simple one. The hook discovery, plug in, plug in discovery object. Here's a test for that. And then at the top I just define one hook that's gonna get called and so I can assert on it. And there's also an example of when you need something like this, when you need a fake function or a fake class, don't be afraid to just stick it at the top of the test. I know that we have rules about where things go but in tests, all bets are off, it doesn't matter just to find a function. So this is what the test will look like regardless of what is happening in the class. We just do a simple assertion on what the expected value comes back and what we expect is that we wanna be told this module, some module implements some hook and it returns some ID. So the first like Drupal 7 style version of doing this you just call module implements. There's actually still code in Drupal 8 that calls module implements. And if you try that, you will get an undefined function because in PHP unit we don't include any code except for the stuff that's in the like the small bootstrap file and the code that's auto loaded. This I would consider untestable asterisk. There are ways to conditionally define functions. We do it in a couple of places but it's a bad idea and you generally never wanna do it. So when you have functions, you have no auto load. When you use require, it's like say you require like the common.inc or whatever has that function, you're polluting your test environment. You're bringing in a bunch of code that has nothing to do with your test and you'll end up with implicit dependencies that you don't know about because you're loading all of the code. And as we saw before, it has unpredictable dependencies. It will make you wanna bring your radio into the bath. And so say we make this small change to it, we change out module implements for Drupal module handler git implementations. This is also something you see a lot in Drupal, especially in the entity system where there's not a lot of injection for reasons. It just calls straight out to this quite often. And so you might wonder if this is actually testable because it is a static method call. It is, here's how. You create a module handler mock, then you set an expectation on it. That's the part you're gonna have to do normally but then you have to create a new container, set the module handler on that container, and then set the global container singleton on the Drupal class so that when that Drupal module handler gets called, it'll be pulling from your container and not Drupals. And actually it'll fail if you don't do this because there is no container in PHP. So that passes. It's testable but it's painful. So you do get autoload but you also have non-object under test code running. And the term you'll see, object under test, system under test, is basically the thing you're testing because you should be testing that and nothing else in a unit test. And then if you do have this, you have to include a setter or else it just doesn't work. So another example, you could hide that call to Drupal module handler in a local method. And here it's basically the same thing except in get definitions, instead of calling out to the Drupal class, it's talking to itself. And then in the module handler, it'll only call out to Drupal module handler if the local variable is not set. And so one way to test with this is in your test subclass that class and add a setter. Some people find it very, very confusing if you just add a setter to that class because I suppose it might imply that you could use that although I think in general, that's probably not a big deal. If you wanna make it really explicit, you can just make, I mean on your normal class, you can just say set test module handler, which is very explicit, that you'd only use that during a test. And then you just basically call the set module handler and your test works. So that is totally testable. It mocks the inheritance or just a test only setter. So last example is just straight dependency injection. Here, this subject normally just had the hook, so we add module handler to the constructor and then you just call straight out to module handler because it had it when it was constructed. And the test for this is pretty simple. You lose the call for the setter. That passes. So that is very testable. Now it mocks just via new, just via making a new class, but it requires a service definition or a factory method, meaning if you have the service definition, that is what's going to allow you to specify which dependencies get injected into it. Or you'll notice with like form, I think forms, controllers and plugins, they have like a static create method. And the idea there is that you have a container aware factory, but your class isn't container aware. And if I probably haven't mentioned that, you don't want your classes to be container aware. Because one easy way to do this would be to just inject the whole container in your class and then you could get whatever you want, but that's not an explicit dependency. You should strictly limit what your class has access to. And so if you have to do that, or if you need stuff and you don't want to actually have a service, the best thing you can do is just create a factory that has the container and then that factory injects those into your class. But this is enough to make the dependency injection police happy. So you might ask, which method should I use? So constructor injection, like that should be your default. Definitely use it for what I'm calling domain collaborators. So like if you're like the token module and then there's a token storage class, assuming there is, I don't know if there is, those are both like sort of in the same domain. And so you have a very, like you have a high cohesion with that class, you probably want to inject that. So the test-only setter, you want to use that when you have dependencies, with safe defaults. So an example of that is say like a logger, a translator, URL generator. These are objects that it's very unlikely that we're going to have more than one instance of in a Drupal request. It's pretty rare. I mean, we might swap them out, but probably not more than once in a request. And it's also a good idea when you have a really bloated constructor. If you have a constructor with nine parameters in it, which we do, the ones that have safe defaults, you might just move to test-only setters because the only reason that you're injecting them, most likely, is that you want to be able to test your object. So if you can test it through a setter, but it's really just like an application like Drupal level dependency, feel free to get it out of your constructor because it's not really that important to what your object is doing. So replacing the singleton via the Drupal class, avoid it if you can. If I think it would probably be better to have a... No, it's not going to be better. The least you can do is to make a local accessor. If you're talking to a dependency, don't ever talk straight outside, only talk to yourself, and then wrap that dependency in a very small method. And that's totally easy, not a lot of overhead, and much, much better. So you might say, but it takes so much work to set up my unit test. This is a sign, and you should listen to this feedback. So all dependencies are not created equal. Like I mentioned before, some are closely related to your class, some are not. So this comes with the idea of cohesion and coupling. So elements are coupled. If a change in one forces a change in another. And cohesion is a measure of whether responsibilities form a meaningful unit. So if two classes are doing something similar, they're close collaborators, they live in the same domain, that's, it's a high cohesion. Entity storage controller has a, or node storage controller is highly cohesive with node. Right, it's not necessarily cohesive to module handler. That is a coupling. So here's an example to pick on Peter Wolonen. Maybe you wrote this, I don't know. The local task manager, it's a new one. And so it'll probably get refactored, who knows. It has a lot of dependencies. Okay, sweet. Good to know. Okay, I might have a suggestion for you. So when I see a class with this many dependencies, my first thought is to look at what they are and how they're being used, because it's pretty rare that you should ever have this many dependencies in one class. It's a good indicator that your class is doing too much. So the first thing that pops out of me is these. So we have request, route provider, and access manager. If you look at access manager, it actually already has a reference to route parameter, or route provider, and I believe it also has a request. And so these three are related. And now if this is a manager object, meaning it probably shouldn't have any significant business logic, it should mostly be delegating to other objects, I would take whatever is happening in these three objects and move them to a different one, and then have the local task manager just delegate to that instead. And then you turn three dependencies into one. Hopefully that makes some sense. Looks like you have something to say here. Right, this is hard, except that it's hard. But I think the point though here is that if you can find things that are related, like these are related and they're not related to those other ones. I think it's pretty likely that you could, that this functionality could exist in an object that doesn't have a controller resolver, module handler, cache backend, and language manager. I think that's likely. So the other one is module handler. And this is a subtle point, but we have this all throughout our code now. So all of the code that would have used module invoke, module invoke all, alter module implements, module implements are all using the module handler now. So basically if you have a hook or you just need a list of modules for any reason, you have to have a copy of the module handler. This isn't a great trend in my opinion. What's a little odd about this is actually it's just using it to call this ultra info, which it turns out is a plugin thing where you set that and then the plugin calls the hook on your behalf. So instead of this, what I would do, and I know I'm just trading one dependency for another here, is I would instead have a dependency on the event dispatcher. Because if you have a dependency on the event dispatcher, all you're doing is you're saying, I have this event that happens and here's the thing that goes along with it, somebody do something with it. All you can do that object is dispatch. With the module handler, if you look at the public interface of the module handler, you have access to way more than you should have. That's basically a dependency on all of Drupal when you have the module handler, versus the event dispatcher, which is much, much simpler. I trust some people will disagree, but I think it's an important point because it's all over the place. And now I don't have too much on this, but I can't talk about unit testing without mentioning TDD. I'm sure everyone has heard of TDD. You've probably heard it talked about a lot when simple tests went in. Another disappointing realization there is that we were actually never doing TDD and it's almost impossible to do in Drupal. I have talked to one person who did it with unit test base, but it's pretty rare. The distinguishing piece of TDD is that you write your test first, and we don't do that. Does anyone write their web test first? Right, that would be insane. What, is there an objection? Does somebody do that? So that's an integration test. It's doable. The feedback loop on that is so slow though, it generally doesn't facilitate that. The idea is you write your test first, and then that determines, so you write it for one class, and then you figure out who that class's collaborators are by that design process. So another good quote on that. You write your test first, you write test before the code. Instead of using testing to verify our work after it's done, TDD turns testing into a design activity. We use the test to clarify our ideas about what we want the code to do. And so this is the trick to unit testing. It's great for refactoring and telling you that your code still works. There is some value there and telling you that your code is correct. The real value is that it makes you write good code, and especially when you write your test first. When you get to writing unit tests and it's very painful, that process will change once you start writing your test first because you'll design better interfaces and you're designing your interfaces from the client's perspective. Because you're saying this object is gonna need to collaborate with this other thing that I haven't written, and this is what I would like the interface to look like. And you always get better interfaces when you do that. And so I would just encourage everybody when you're writing PHP yet, or you have a new class to make, you're porting your modules, just give it a shot. It's gonna be painful at first. You'll probably give up on it, but just keep trying because once it clicks, it's totally worth it. So just a couple of references for things I mentioned directly in the talk and things that have influenced my thinking on this. The growing object-oriented software guided by tests. That's probably the top one. Ex-unit test patterns. There's the paper, macrolls not objects from UPSLA that explains mocking what you own. And then the Sandy Metzbook practical object-oriented design in Ruby, which really is not that Ruby specific, but it has a great testing chapter. So that's all I have. Take questions and then feedback if you can follow those instructions if you want to give feedback. Yeah, questions. I can stand here awkwardly for a really long time. Oh yeah, and if you wanna ask a question, you have to go to the mic. Yes, yes. Right, so are Stubs and Mox useful in their own ways? Like, so you use one in one situation, one in another, not use one or the other. Yeah, yeah, absolutely. And so that's, that's right. I think fakes are fine. I think it's all depends on what you're dealing with. If it's really simple for you to just create the fake and not deal with it, or if you're gonna, especially if you're gonna reuse the fake. Like having a memory-based, say we had a memory-based database connection, right? That would be really useful because we'd use that all over the place. And so maybe it's worth making a fake in that case. But it's like most design questions. I mean, because this is basically a test design question. It's you should let your pain guide you. If it hurts, do the other thing. But between Mox and Stubs, like there's a pretty important distinction there. You use a stub when you wanna verify the behavior of your method, but you need input from somewhere else to accomplish that task. And you use Mox when you wanna verify how your object interacts with another object. I think like I could say, you should try to always use Mox and that's like a super object-oriented geek thing to say. But it's a really hard thing to do. And so you should try, but you're probably still gonna need Stubs here and there. And really a Mox is a specialization of a stub. Sometimes when you have Mox, you need somewhere to return value so that it does what it does. And also you said the three things. There's actually more that I left out. Right, right, right. There's a dummy object. It's like when you just need to fulfill a constructor, but it's not actually gonna be used. That's called the dummy object. It's probably not that important that you know any of that. Did you just give up on your question? Wait, I just answered it in. Oh, okay, yeah. So I thought I saw Pat Collins, Connolly over there. I just frapped Michael over there. All right, that's disappointing. Anything else? Okay, thanks.