Why do you write your tests like that?

Joel Clermont (00:00):
Welcome to No Compromises, a peek into the mind of two old web devs who have seen some things. This is Joel.

Aaron Saray (00:08):
And this is Aaron.

Joel Clermont (00:15):
I'm feeling a little contrary this morning, Aaron. And I want to share some opinions that maybe will be outside the mainstream and this episode will convince everyone that we are right.

Aaron Saray (00:29):
Outside the mainstream, or? And hold on, because I'm going to sound like a cranky old man here.

Joel Clermont (00:34):
Okay.

Aaron Saray (00:34):
Or, just outside the Twitter stream?

Joel Clermont (00:37):
Oh, yes. That's right, it could very well be that. Because there's two things we were talking about and they both relate to testing. And it's something that we've observed maybe, is the right word, across different projects and different teams. And honestly, I don't know. I'm kind of joking because I don't really have deep, strong passionate opinions about these things, but these are some things I've noticed that I think bear comment. Let's take the first one and they both have to do with testing. The first one I've seen this weird, or I guess unexpected, maybe weird isn't the right word, pattern where a PHP developer... No, let's get more specific Laravel developer, uses CamelCase methods everywhere in their application, except when it comes to testing. And all of a sudden like, "Well, here snake casing methods makes a lot more sense."

Aaron Saray (01:31):
Oh, and you have to annotate it with @test as well.

Joel Clermont (01:34):
Yeah. I mean, you could still start it with test underscore whatever, but yeah, I've seen that too. "Let's use the annotation here," and nowhere else in your code are you using annotations.

Aaron Saray (01:45):
Right.

Joel Clermont (01:46):
I have a theory for why this exists and I think it's a historical thing. I think because a lot of people that are, I don't know if thought leader is the right word. But a lot of people who early on in Laravel's existence promoted testing and test-driven development, maybe looked at the Ruby on Rails ecosystem and because they have a lot of good ideas, right? That's fine. We've even talked about that, it's good to look at other communities. But they use snake case test names, but they also use snake case method names everywhere. It's just kind of a weird, I think of it like a historical artifact. But then when you call a developer on it like, "Hey, why do you do that?" They'll sort of retroactively come up with reasons why it's actually a good idea and... What's your take on that, Aaron? First of all, have you seen this? And second of all, am I out on a limb here or are you with me?

Aaron Saray (02:37):
No, I've totally seen this.

Joel Clermont (02:39):
Okay.

Aaron Saray (02:39):
I've seen this before. Well, it is typical mind fashion. At first I reacted with anger. "What are you doing? Aargh." But obviously, you tame that down and take some time and just look at it objectively. And yeah, I've seen that and I've called people on it too. I said, "If you're using your PSR standards on everything else, why do you just stop them here?" And the explanations I've gotten is, "It's more readable."

Joel Clermont (03:06):
Yeah. Number one here, that's what I've heard as well.

Aaron Saray (03:11):
Well, I can maybe buy that if it begins with the word test but I don't buy that if it doesn't begin with the word test, and then you have to annotate it with test too. Because I think that's actually more difficult to follow because there's a bunch of methods that all have the same annotation. I mean, you could argue that, "Well, then if you have some other methods that you are using for helping building some data or something, they won't have that annotation. So, it's really easy to look at the list and see which ones are test and which ones aren't." But I still haven't understood, because as far as I understand, the built-in methodology was follow your code standards in your code and in your tests. And if you begin with the word test, it's going to recognize that as a test.

Joel Clermont (03:50):
Right. That's just how PHP unit works. And the readability thing, I wanted to maybe dissect that a little bit. Because my initial reaction to that is, "Okay, if it's more readable, then why not use it everywhere?" And arguing with myself as I like to do, you could argue, "Well, method names in tests tend to be more verbose." right? They're describing more of a scenario, but not always. Like testSuccess, or testIndexUnauthorized. I don't think that's that much longer of a name than a model relationship name, or... I mean, controllers will leave out because we, again, argue, "You should use just the resourceful methods or __invoke." But other places in your code, you probably have multi word method names and it's not hard to read, so I don't think that holds up. But then again, do what you want to do. We've come into these projects, it's not hard for us either.
(04:47):
It's like, "Oh, I can't read this. What are all these underscores?" I'll just throw one other thing out there and it's a minor thing. But typing it is harder, right? Hitting shift and just a capital letter is a lot easier than shift underscore, I think. If you sit at a keyboard and you type. It's not a lot harder but it's extra characters and it's just like a weird rhythm to have to type. I might be going off on a rant a little bit here. But one of the artisan commands, I can't remember it's models or something, you used to have to type it with underscores. Then all of a sudden it's like, "Oh, you can type it snake case." And people are like, "Yes, finally." I'm like, "Well, so clearly it is better to type in snake case for reasons." Anyways, that's my rant on the underscores in the test methods. You have anything more you want to say, or anything you want to correct on me, Aaron? Because I know you love doing that?

Aaron Saray (05:44):
I love the way you make our podcast sound like I'm just a huge jerk all the time. "I know you love correcting me all the time."

Joel Clermont (05:50):
I'm playing the long game, yup.

Aaron Saray (05:53):
No, I don't have anything more to add on to that. But I think test says in general is a pretty good area to focus on some other sort of things where... I guess I would say this naming thing, if you did it with the underscores because that's how you learned or whatever, it becomes one of those things where it's like you just do this because that's how you learned and you don't really dissect it any further. But we're always with the mind that you should learn a thing, do a thing, then dissect a thing, right?

Joel Clermont (06:18):
Mm-hmm (affirmative).

Aaron Saray (06:20):
Learn how something works, start doing it a couple times and then dissect the way you're doing it to make sure that you know you're making sense and whatnot.

Joel Clermont (06:26):
Sure.

Aaron Saray (06:26):
I think that's the same thing with those method names. I also think it's the same thing with test content in general. A lot of times we'll see people have very strong opinions about what actually goes inside of a test method itself. Whether you should make special methods in your function or inside your class to, I don't know, share a code or prepare things. Or, if in a constructor or setup method, you should build a bunch of stuff first all the time even if you're not going to use it in every single method. Have you run into that? Where you maybe had some conversations about that?

Joel Clermont (07:05):
Yeah. I think I would put that under the bucket of trying to reduce code duplication in tests because it's like an instinct we've built up in our application code. Duplication, especially once it reaches a certain point is annoying. It can lead to bugs or multiple slight variations in how something is done. So there's a lot of repetition in tests. Like you mentioned, it could be setting up the world, right? Building a bunch of factories, relating things together, it could be setting up users that are going to do a certain thing with permissions and roles and all that good stuff. I've seen this where a developer will get a little clever and be like, "Hey, you know what? I wrote the same testUnauthorized method 50 times. What if I create a testUnauthorized helper and I pass it a route?" Or, "What if I create one testUnauthorized method and then I feed it a data provider that has a bunch of routes in it?" That makes things smaller and it reduces duplication, but I actually think it makes it less readable and harder to understand and browse the structure of your tests.

Aaron Saray (08:14):
Yeah. I mean, I've heard arguments where people say, "Well, if you can't browse one method in your test, how do I know you're writing good code?"

Joel Clermont (08:22):
Sure.

Aaron Saray (08:22):
You should be writing good code in your test as well as good code in your application. But I think they're a little bit different. Whereas I kind of look at a test, even a single method, as sort of an external observer of functionality that measures it. Each individual test is a whole application to me in a way. I mean, I'm not going crazy, we have a whole scaffolding around it so it's not really. But I still think of it that way. It's like, every single test method may look similar to another one if I'm just testing a slightly different outcome because I want them to be individually... And I want to be able to just look at that one block of code and know everything I need to know in order to test that functionality that the method's named after is right there in front of me and I don't have to go and find it. Kind of like you said. But I also know that anything that I want to do to that test, like editing it, changing stuff or whatever, is only going to affect the test I'm working on.

Joel Clermont (09:17):
Right. Yeah.

Aaron Saray (09:17):
Because, I mean, that's more annoying too. You go and fix something in this test and you start running and your previous tests that were passing now fail or something like that.

Joel Clermont (09:24):
Because they have some shared overly complicated set-up method that there's weird interdependencies between them.

Aaron Saray (09:32):
And to say, I don't think data providers are bad when it comes to data, like user input. But it shouldn't be for core things that the user couldn't iterate through, or something like that. Or your system couldn't iterate through. So routes are all individual, they're not iterated. They're just one hard code thing. Whereas maybe a mathematical function, where you're going to test positive, neutral and negative numbers, that's something you could iterate through with a data provider.

Joel Clermont (10:00):
We've even done them when we're writing tests for validation stuff to like, "Well, what if I throw these four slightly different bad email addresses, does it catch them all?" That sort of thing. Yeah, I'm definitely not railing against data providers, but I think you can take it too far and I've definitely seen that. I like your analysis that kind of thinking of it as a self-contained application, it makes it less scary to have a little bit of duplication. And I like that. And just full confession, there have been times where I've done this. I created a helper method, I can't remember what it was on, recently and you were like, "Joel, why would you put that in the base test case? You're only using that in 38 tests or something." And I'm like, "Shut up, Aaron."

Aaron Saray (10:44):
I was so mad.

Joel Clermont (10:45):
I know. But just full disclosure, I find myself with these tendencies as well. And it's like, I just don't really need to do it. Because first your initial reaction is anger, mine was as well. Like, "Shut up, Aaron, this makes sense." It's like, "I had to write this 30 some times." Then in the end it's like, "Well, if you really need a helper, limit the scope. Put it in the test class that needs it, don't put it in the base." You'd have to really use something everywhere to justify that.

Aaron Saray (11:16):
Right. I think the last thing I'd mention about this whole idea of the individual tests being sort of an observer on the outside and handling all this stuff, is the last thing I suggest not to do is you should never be editing your, well, this is a tough one. One of the purposes of unit testing, it actually helps not only test that the code is working, but it really kind of helps you compartmentalize in sort of making the modules your code. Because you have to think about more what's going in and what's coming out, and smaller and smaller sections that you can test easier. That can help your test become... or your code become a better quality. That's what tests should do to your code, make it a better quality in its existing functionality. But what they shouldn't do is be responsible for introducing something new to your code base, just so you can easily test.

Joel Clermont (12:07):
Oh boy, I've done that.

Aaron Saray (12:09):
Yeah, so I've seen that. Where a model has a bunch of methods and then there's one that is just used for tests. And that's not what tests are for.

Joel Clermont (12:17):
Or, even changing visibility of a method to make it easier to get at from the outside world.

Aaron Saray (12:22):
Oh, yeah.

Joel Clermont (12:22):
You've yelled at me for that too. That's a good point, yeah. Your tests shouldn't drive things in your application to that direction. In hindsight, I don't think these opinions are that weird, but maybe they're new for you listening to them. And I think it's worth sharing. I like your point, Aaron, earlier about dissection. If you catch yourself doing something, just think, "Why am I doing this? Does it make sense to keep doing this?"

Aaron Saray (12:48):
Mm-hmm (affirmative). Let's be clear real quick on this last thing here. Is, we're giving some opinions on how to make this process better but we're still proud of you for writing tests.

Joel Clermont (12:59):
We definitely are.

Aaron Saray (13:07):
I don't know if this goes across all cultures but definitely I've seen this in America. Is when you have a dude just witnessing something very interesting, very talented and maybe he is with a group of friends or whatever. Maybe he was like, "Oh, come see this show," or something. He's sitting there and then something amazing happens, you'll see that the dude that brought everyone there, looks at everyone and they all look at him. Like, "Are you all enjoying that moment?" But then he nods and goes, "Yeah." As if he had something to do with that.

Joel Clermont (13:40):
Sure.

Aaron Saray (13:40):
Have you ever noticed that? It's like an impressive thing and he is like, "Mm-hmm (affirmative)." And you're like, "Wait, why are you nodding? You had nothing to do with what I just saw."

Joel Clermont (13:47):
Well, they looked to him first so they kind of set him up to take credit for it.

Aaron Saray (13:51):
Yeah, I guess. I guess I just seen it on TV and stuff too. Where it's like, "Oh, that's good." Oh, he was on The Voice, I think. It was like one of the guy's singers was singing really good and the other coaches look over and he's like, "Yeah, Mm-hmm (affirmative)." It's like, "You didn't do this, that's them." What are you nodding for?

Joel Clermont (14:07):
Exactly. Stay out of it, man. You're just an observer.

Aaron Saray (14:13):
What's that called when a camel has two humps? I don't know. But if you're looking for help with other CamelCase related stuff, we can help.

Joel Clermont (14:21):
If you'd like someone to review your test suite or if you need help building a test suite from scratch, head over to nocompromises.io and book a call with us.

No Compromises, LLC