Going further with tests and cleaner code

Learn With Jason S8E4 Feb 20, 2025

Part 2: an exploration of TDD as a way to clean up our code — and our thinking. Cory Speisman returns.

Read the transcript

Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, everyone. And welcome to another episode of Learn With Jason. Today on the show, we're bringing back Corey. Corey, how you doing?

COREY: I'm doing good. How about yourself?

JASON: I'm pumped to have you back.

COREY: I'm pumped to be here.

JASON: For folks who don't remember, we did so, just one second. I stopped paying for Twitter because I just can't pay for Twitter anymore. It means that I can't stream to Twitter anymore. So I had to, like it was throwing a bunch of errors at me. Anyway, I'm actually ready. Let's yeah, okay. Sorry, everybody. Technical difficulties. Corey, let's talk. You've been on the show before. We did what was honestly like a super enjoyable show digging into testing. Not like testing as a chore you have to do but testing as an actual tool for helping us think. Let's talk a little bit about who you are for anybody who didn't catch that first episode. Then we'll get into what we talked about.

COREY: Yeah, hey. I'm Corey. I like to play golf, and I like very smokey scotches, and that's about it. Oh, I guess I'm also a software engineer in my day job. I work at Netflix. I try to spend my time writing code, but more so than ever, it's a lot of talking to people, listening to people, trying to act confident but also enough humility to take feedback from other people. So it's a big balancing act. It's fun.

JASON: Absolutely. What's the what's your current favorite smokey scotch?

COREY: Oh, man. I'm going to butcher the name. Laphroaig. It's like a campfire.

JASON: Ah, my first experience with Laphroaig, I was like 21. A friend of mine got his first apartment. He got a bottle as a house warming gift. We sat down thinking to ourselves we're going to be grown ups and drink scotch. At this point, like, mind you the stuff that we would drink was light beer and like Mike's Hard Lemonade. We were not like alcohol drinkers, you know. So we got our Laphroaig. Not understanding how scotch works, we poured ourselves a solid four ounces of scotch. Nothing in it, just straight up glass of scotch. Then proceeded to, like, both hate it with our entire like every part of us was like this is the worst thing we've ever done, but we weren't going to be the one to say we can't drink this. It took me another 15 years to drink scotch again.

COREY: I can picture you sitting on the couch dying inside like, it's not that bad, pretty good.

JASON: Yeah, it was like drinking the inside of a cedar cabinet. (Laughter)

COREY: That is so accurate.

JASON: Anyway, this was great.

COREY: What were we here for again?

JASON: Yeah, we're here. This is Corey Speisman. Let me actually put the branding up so everyone can see who you are. There we go. So, we are talking about tests and cleaner code and just how that whole process can make us a little better at writing good code. So this is a part two, where we're going to pick up on the repo we worked on last time. As a quick refresher, what did we cover last time?

COREY: Yeah. So, it's probably worth doing my spiel again of like why we want to write tests.

JASON: Yes, please.

COREY: The reason for writing tests isn't to write tests for test's sake. This gets bombarded a lot, like test for good test coverage. I've seen a lot of high test coverage code bases that just, like, aren't a really good test suite. I think the way that tests have been really useful for building software is my goal is I want to deliver features as fast and as predictably as possible. The way we learn, the way we build better software is to put things in front of people, get feedback, learn. So if the goal is to move as fast as possible, I want a really easy code base to work in. Old, spaghetti like code is hard. It's going to slow any good developer down. I say it'll probably slow like the best developers, it'll slow down even more because they're going to want to make things nicer. But in order to have easy to reason about code, I need to be constantly refactoring. I need to be constantly rethinking like the encapsulations, the abstractions I've made, and the way that I'm building my public facing APIs around business logic. Okay. In order to get that, I need confidence that I'm not breaking anything. In order to make sure I'm not breaking anything, that's where I need tests. And that's where I need good tests. So it's this whole long cycle of why I need tests, but they become so important for creating clean code bases to reason about.

JASON: Right.

COREY: Yeah, so where we left off last time is I was walking us through a sample project of my and my friends trying to eat every menu every item on the menu at Cheesecake Factory.

JASON: How's progress going?

COREY: It's so good.

JASON: Like 1% complete, I assume?

COREY: Yeah, maybe two now. It's like the good menu items at Cheesecake Factory, I think I started to find some unicorns that no one thinks to try. French dip cheese burger. It is so good.

JASON: I'm not going to lie, Corey. I've got my doubts.

COREY: Okay, you've got your doubts.

JASON: (Laughter). All right. Next time I'm at Cheesecake Factory, I will try the French dip cheeseburger. It feels like I need to go to church after I said that.

COREY: I felt the same way. I took a bite, and I was like had I not been doing this exercise of trying to eat every item off the menu, I would have never chosen this item. I'm so glad I did.

JASON: Okay. All right. Okay. You know what, that's a good way to find something. You know what, I should swallow my prejudice on this because a lot of the food that I love is food that, like, when you hear what it's described as, you're like that sounds disgusting. Then it's really, really good. I'm going to go to the Cheesecake Factory and try the French dip cheeseburger. So we've been building this project. What we've been able to do so far is we've gotten a few behaviors out that we wanted to do. We mocked up an API. We were able to get I think we've got the items loading from the Cheesecake Factory menu. I think that's about as far as we got. Did we add the check box functionality yet? I don't think we did.

COREY: It's worth noting that if people haven't watched the first one, it's probably worth going back and watching the first one. We took a very layered approach to this and really started to rely on our contracts that we were building. We would create interfaces and say this is our menu fetching service or our menu client. We would run an interface of the high level policy for how the consumer of that would work. Then we'd write the actual implementation. So we were in the and this also allowed us to, like, inject things like fakes or stubs into our test. We just got to the point where we were working on the persistence layer. So hey, I've eaten an item. Hold on to this so when I get back to my list, I know which items I've eaten.

JASON: Got it. Got it. Okay. So why don't we do this. I want to make sure we have a lot of time. Let's jump over and actually look at our code. So the first thing I'm going to do, this is the original episode. I've dropped a link in the chat. This was in season seven last year of Learn With Jason. It's on the Code TV website. You'll be able to find it in the link I've dropped in the chat. It'll also be in the description. If you need captions, we've got a live captioner. Thank you, Rachel, from White Coat Captioning, for being here today. You can find that at lwj.dev/captions will take you to that site. So let me drop that on the screen there. We're talking to Corey today. I'm going to drop a link to Corey's LinkedIn. So you can go and find Corey if you want to follow, be friends, all those good things. Did that not work? What's going on? No, here it was. Yeah, okay. Then, this is the repo that we were working on today. This is the repo we built. It is a work in progress. We've got a few of the tests running. We've got some of the functionality going, but we still have a good list of things we want to get done. So let me pull this up in my code editor, and I'm going to go ahead and oh, I guess I'm going to close it all the way and we'll try that again. Get out of here. There we go. And we're going to open recent, pull up this testing cleaner code. And here we go. On the right hand side, we have what we've built so far. So this is a what have we done here? This is an Astro site. It's using React. We've installed Playwright. We have some JS dom stuff going on. We're using V test. Maybe if we want to do a quick recap for anybody. We have some raiders. Welcome, raiders.

COREY: Do you want to start the dev server and just see all the gloriousness we've done so far?

JASON: What an excellent idea.

COREY: And if it breaks, that tells us what we need to go fix.

JASON: Okay. So we do we are out of date, but everything is still running.

COREY: Cool.

JASON: If I head out to here, we can go to local host 4321. That's going to give us

COREY: Every menu item at the Cheesecake Factory.

JASON: Let me blow this up so we can read it a little more easily. Mortadella Panino. All right. This menu is so wild. Chicken shawarma to Thai noodles. It's so much.

COREY: It sure is. Okay. We also have a to do readme file, which I usually like to do on my projects to make sure I stay focused.

JASON: Which is especially wise. Oh, todo.md.

COREY: So we did the first part.

JASON: We got really far on this first episode.

COREY: But we had great conversation and laughed a lot. I went back to watch it so I knew where we left off. We laughed a lot. It was great. Okay. So we are at the part of I want to mark menu items as eaten.

JASON: Okay. So to do this, we need, you mentioned, a persistence layer.

COREY: Yep.

JASON: We're going to need the ability to we'll need some UI, like a checkbox. Then we need to be confident that when I click that checkbox, we are sending actually, this is probably worth a conversation. Do we want it to be when I check the box it saves, or is it check the box and hit submit and it saves?

COREY: Yeah, I don't know yet. We'll figure it out. Let's do this. Let's go into our menu service, the area that's holding all our business logic.

JASON: Yes.

COREY: It's staying UI agnostic, where it's like the only thing it doesn't care, like, who's consuming it. It doesn't care about if it's Astro or React or anything like that. It is literally just holding on to our business logic. Like get me items, get me my eaten items, mark items as eaten. Those are the only three things. I think if we open up the test file for this, we have it loads all the menu items. It marks an item as eaten. So just to walk through this one, we create a menu client stub, which fakes the items coming back from Cheesecake Factory website. Then we have a eaten items fake, which is a fake persistence layer. Right now, if we dive into that definition... that is just holding on to items in memory.

JASON: Right.

COREY: Okay. Let's run the test in menu service test and see if they pass or fail or where we left off.

JASON: Okay. And to do our test, I think we do we have that just set up as

COREY: Yeah, it should be in the package JSON.

JASON: Npm run test?

COREY: Yeah, npm run test.

JASON: Okay. So right now all of our tests are passing. You can see that it runs the menu service as well as the menu view.

COREY: Okay.

JASON: We've got two test files, three tests, and all of them are passing.

COREY: Perfect. If we were smart, we would have left the other episode with the failing tests so we'd come back and be like, oh, we know exactly where we need to go, which is one of my favorite things to do on a Friday afternoon. I'm just going to write this test to fail so that way I can come back and know exactly. So I think our menu service is in good shape for us to now write the UI part.

JASON: Okay, yeah. Let's do it.

COREY: Let's hop into the menu view. It's up in lib menu. You also gave me a hard time last time because I like to put everything into a features directory of being like lib menu and that's where my components live.

JASON: You know what, I started out by giving you a hard time, and I'm fully sold on it.

COREY: Nice.

JASON: After we talked through your reasoning. I actually since then, I've realized a couple times where I spread out logic across a handful of thing, and then I had to migrate it. I was like, this is hard to find. I should have listened to Corey and put it in a lib folder.

COREY: Yeah, sometimes I know what I'm talking about. Not often, but every now and then I do.

JASON: (Laughter)

COREY: The best part is I pretend I know what I'm talking about and sound confident.

JASON: That's the secret to it all right there. Appear confident.

COREY: Let's open up the test file. Let's write a failing test, shall we?

JASON: Okay. So in the view test, right?

COREY: Yep. Okay. So we're getting a

JASON: What do you want?

COREY: TypeScript error because our menu service now takes two dependencies.

JASON: Mm, oh, because it wants the eaten items store.

COREY: Exactly. So let's just drop in we have that fake that we wrote in the last episode.

JASON: Service module, and it was eaten items fake.

COREY: That's happy. That should in the testing world, we call this line 11 a dummy. It's needed to fulfill the contract of menu service, but it's not going to get exercised in this test whatsoever. Like at no point does this test actually call this because it's just exercising the idea of we render this component, and we get all the menu items.

JASON: Got it.

COREY: Okay. Let's create a new test and say it marks an item as eaten.

JASON: Okay. That's probably going to be async. Is it a safe assumption to say they'll always be async when writing tests?

COREY: It's just so much easier.

JASON: There's not really any downside to not doing it, right?

COREY: If there is, I don't know and I'm not smart enough to know the intricacies. But there's been so many times I've gotten binned by not making my test async and not awaiting certain things and trying to assert on things that are going through a promise and my test actually completes before a UI re renders or something like that.

JASON: That's like my Achilles heel. I will always forget that I've returned a promise, but you know, oh, it's passed. The log shows up a half second later, and you're like, wait a minute. (Laughter)

COREY: Yeah. That and using object references is the thing that always gets me. I'll set state with an object it already has and nothing re renders. I'm like, what? But I digress. Let's render the menu view, like we're doing in the test above. Line 15 to if you scroll up a little, we can probably just copy/paste what you have above.

JASON: Do I need all this?

COREY: Yeah, let's bring that.

JASON: Okay. So here's a question. At what point do you start doing because I know that some of these testing libraries will have like a before all or an after all or one of those sorts of things. At what point does it make sense to start thinking about one of those?

COREY: That is a fantastic question. I have no idea.

JASON: Like, I guess when it starts to be more inconvenient to not do it.

COREY: It's funny. I like to feel the pain before creating the solution. So this wasn't that bad. If we created a third one and I was like go copy and paste it again and it's you can see like files things are identical. We're taking up, I don't know, 50 lines of just copy and pasted code and things are hard to reason about. I think that's when I'm like, okay, this needs to get put into a before each block.

JASON: Especially when you start to like, later, if we do start making these do things, we're going to have to edit it and edit it everywhere, and it turns into a whole thing. But, okay. I'm with it. So we've got our menu view.

COREY: Okay. Now, you asked the question before like do we just want this to be a check box, you check it, it fires off, or do you want to check a bunch of things and hit submit and it fires off.

JASON: My hunch is that there's not really any cost to unchecking that box, and it's not a destructive exercise. So treating it like a to do list where you check the box and it persists and then you can uncheck it if you're like, oops, I didn't mean to click that one, that seems fine to me.

COREY: Yep, totally. That sounds great to me.

JASON: Okay. So then in comment code, we're going to we want to find the item with the name we're looking for.

COREY: Yep.

JASON: Find the checkbox for that item. Then we want to click that checkbox. Then we would want to assert that the or, we wouldn't even be asserting.

COREY: I would.

JASON: I guess do we want to

COREY: Yeah, I know exactly where you're going. Hit me with it.

JASON: We would assert the item had persisted, but that's not UI. So instead, we should assert the box is checked.

COREY: I'll stand up. I'll clap.

JASON: (Laughter)

COREY: That is perfect reasoning. Like, we don't want to introduce that business logic sorry, we don't want to tightly couple our test to that implementation. Because we are testing the UI, we don't want to test against our persistence layer, meaning if this thing starts to talk to local storage or if it starts to talk to a SQL lite database or starts to go to S3 for some reason, we don't want to introduce that knowledge into this test, and we are only concerned about the UI. Okay. So, what you have is perfect.

JASON: Excellent. Okay.

COREY: Sorry, that was a long way for me to say what you have is great.

JASON: No, no. This is great. So let me can I do a visual split? I'm not going to try to do it that way. Instead, I'm going to open it this way. Then we can see the view and the test side by side because I think that'll help. Is that too small for everybody? Like to give us a little more reading room. If that's too small, let me know in the comments. I'll make it bigger again. Otherwise, I'm going to optimize for a little more text on the screen for us. But here we go. We've got a list. So if I want that test to fail now, I would say we want to let's see. We're going to use screen. Does this return? Like if I say item equals await screen dot find by text.

COREY: Exactly.

JASON: And we're just going to use cheesecake again.

COREY: Oh, can I correct this real quick?

JASON: Yeah, please do.

COREY: If we want to watch this fail immediately and also have a test in the right direction, I would change this to find by label. We know our implementation is going to need a checkbox.

JASON: Oh, right. Because we want to wait.

COREY: Find by what is it? Maybe I have the wrong API.

JASON: Get by label? Find by label text.

COREY: Yep, find by label text. Thank you.

JASON: Okay. So find by label text. That's going to be because we want the checkbox label. We're going to make sure we write accessible code because we are, you know, good developers.

COREY: Yeah, sometimes.

JASON: But the other good thing about this is that this should fail because we don't have a label. Also, now we don't have to actually do this part because if we're using a label, we should just be able to click it. Then we want to assert item no. How do I let's see. So now I need to find

COREY: Yeah, this gets tricky. Do you mind commenting out line 37 and 34 for a second? I can either hold your hand through this, or you can keep doing what you're doing.

JASON: Yeah, let's have you hold my hand. I want to get further. So I think that'll work better. How do you want to do it?

COREY: Let's run the test. It's called like calling your shot. I know this is going to fail. I know how it's going to fail. That way nothing tricky stands in our way. Okay. So this failed because it can't find the label text.

JASON: Okay.

COREY: And hopefully, our tests above continue to pass. We didn't break any other test. Okay. Let's make this assertion pass real quick.

JASON: Okay. And we're going to do that by we've got our items here. So what I'm going to do is return label. That label is going to be like this. Then I would do actually, we're going to put the input at the front. So like an input type equals checkbox. And we're going to give it a name of name. No, value of name.

COREY: Yep.

JASON: And do we need it to be like, do you want to get into labels and stuff right now or actual names and stuff?

COREY: Uh, when you say actual names and stuff, are you talking about an ID?

JASON: We would need a name for this so when we do submit it, there's something to do.

COREY: No, I think we'll be okay.

JASON: Okay. So let's just rock it like that. Now our test passed because it finds the thing.

COREY: All right. I would say let's make sure that this isn't checked. So let's do like an assert item dot checked.

JASON: Well, it wouldn't be the item. Will the label get the pseudo class of checked?

COREY: I think this will give us back the input. Let's do this. Yeah, let's do assert item checked real quick.

JASON: I don't know that's going to

COREY: Oh, we might have to typecast this to an HTML input.

JASON: Well, so I'm finding it by the label though, right? So I have the HTML label. Do I need to then find the input contained within it?

COREY: I honestly think this will give it to you.

JASON: Oh, okay. Should we just try it?

COREY: Yeah. So it's not going to be a function. It's just going to be a property.

JASON: Oh.

COREY: Then we can do, like, is falsy.

JASON: Like that?

COREY: Yep. See what that gives us. Oh, sorry, it's assert. My bad.

JASON: Expect is not defined? What am I doing here? Oh, we don't have it yet is why.

COREY: Okay. Now what is it telling us?

JASON: Now you're mad because invalid property is falsy. What am I doing wrong? To be falsy?

COREY: Yeah, it could be. Sorry about that. Okay.

JASON: So that worked. So this is interesting. This thinks it's an oh.

COREY: Let's double check our assumptions and make that menu item in our implementation checked and just watch this fail real quick. Okay. I'm guessing that's some high level thing that React testing library is doing for us.

JASON: Okay. So should I do like as HTML input?

COREY: Yep.

JASON: What are you? Input element. Then you're not mad at me anymore.

COREY: Okay. So now let's do the click.

JASON: Let's do the click. Then we would

COREY: And this is the confusing part. Our call to this storage is asynchronous. We have to wait for the item to be checked.

JASON: Okay. I know what you're talking about, but I can't remember how that works. How does that

COREY: That is on line 40 or 41. Actually, we'll wrap up. It's a wait. Then the method name is wait for. Then that takes a call back. And that call back is our expectation.

JASON: Okay. And that will mean we'll do this. Then I'm going to have to import this, which hopefully just works good.

COREY: Okay. It's going to be we're waiting for it to be truthy.

JASON: Waiting for it to be truthy. And it passed.

COREY: I think that's because we still have the check.

JASON: Nope.

COREY: We don't? Oh, that is crazy.

JASON: Well, because it's doing the thing, right?

COREY: This isn't tied to any storage yet.

JASON: Well, yeah. Because right now we're just checking that this is checked.

COREY: Haven't actually hit anything.

JASON: Right.

COREY: Okay. This is good. Let's write another test. So I was expecting this to fail. But it's not. So I'm going to write another.

JASON: Because right now we're quite literally just testing the HTML does what we expect.

COREY: Exactly. Let's write another test to actually force it.

JASON: In the view?

COREY: Yeah.

JASON: Okay.

COREY: And we'll make this thing fail. So I think it's good to hold on to this test, and we're going to fail it in a second. But we're going to write another test that we know will actually fail.

JASON: Okay.

COREY: So, I would say the test should be it renders eaten items.

JASON: Okay. So we're getting into our little guy here. We're going to copy/paste again.

COREY: Okay. And this one, instead of just instantiating the fake store, let's instantiate outside of that constructor and save it off to a variable. So line 48, just bring it out.

JASON: Bring it out to...

COREY: Just like the scope of that test. Like to line 45.

JASON: The scope of this test. Okay. Like in here.

COREY: Yep. And let's save that off to a variable. Then let's populate that with some fake data.

JASON: Got it. Eaten items...

COREY: And let's just toss cheesecake in there. Okay. So now when we render our view and we get cheesecake, that should be checked by default.

JASON: Right. So then we're going to copy/paste this again.

COREY: Yep.

JASON: And we would expect this

COREY: To be truthy. Jason? Did I lose you?

JASON: Sorry about that. My whole camera setup just got weird.

COREY: I wasn't sure if it was me or you.

JASON: Okay. Can I hear you now?

COREY: Can you hear me?

JASON: I can hear you. Sorry about that.

COREY: It's all good.

JASON: It like I think the cable going to this computer just did something weird. Because when I unplugged it and replugged it, everything came back.

COREY: That's so crazy. I thought we were in big trouble.

JASON: I was also a little worried there. So, this one we would expect to fail. Because we have our menu view. We've got the cheesecake. Now, the cheesecake has been manually marked as eaten up here. So we would expect this to already be checked, and it is currently not, which is why this failed.

COREY: Perfect, yep. Basically, testing the controlled input of everything, whereas before we weren't testing in I controlled input whatsoever.

JASON: Right.

COREY: Okay. So let's make this one pass.

JASON: Okay. And to do that, we need to get our menu items. We are our menu items. Our checked would equal do we get back just right out of the gate whether or not it's eaten?

COREY: I think we'll need to so, right now we're getting our menu items through a hook, the use menu items. I think that would have to return us our eaten menu items.

JASON: Okay. So right now this is getting the items.

COREY: Yep.

JASON: And it is not giving us anything that would loop through the items to give us okay. So we need to also get how did we do this? Get menu service.

COREY: Yep.

JASON: And it does send back the eaten items fake.

COREY: Yep. So this gives us back a new menu service. So I think we just need to get the eaten items off of it and probably just put that into state and return it.

JASON: Okay. So that coming back then means that we have in the service we should be able to eaten items would be await service get eaten items. So it was there all along. Then we're going to is this am I in the right direction here?

COREY: I think this is a great direction, but I also think yeah, let's do this direction. Let's say this is our first iteration of this direction. Let's do the pattern we see in front of us. We can talk about trade offs later.

JASON: I already have some misgivings about what we're doing right here.

COREY: Okay. I think you might just have to type that state.

JASON: Huh, okay. That's fine. Oh, wait, wait.

COREY: Yeah, go up. It is

JASON: It's just a set of a string?

COREY: Exactly. Because it's just the names of our menu items.

JASON: Oh, right, right.

COREY: And you can get rid of that array that's wrapping it.

JASON: Because it is a set.

COREY: Yep. And then exactly. And I think we're missing an alligator guy.

JASON: An alligator.

COREY: Sorry. Less than.

JASON: Right, the pointy boy.

COREY: Yes, the pointy boy. We all got our own terms.

JASON: (Laughter). Okay. So we got our eaten items. We got the set working there. So then what we're going to do is we're going to say checked equals

COREY: I think we need to return it from the hook real quick.

JASON: Didn't we?

COREY: Sorry. On line 22, we have to return the state.

JASON: Line 22.

COREY: We're returning the menu items. And we need to return the eaten items as well..

JASON: Oh, oh, oh. I understand. Yes, okay. So we can do this as so, get one of those. Get one of these.

COREY: Perfect.

JASON: Then down here, we're going to check if eaten items has name. Okay.

COREY: So which one passed and which one failed?

JASON: So we've got

COREY: Let's do a cleanup too. This happens.

JASON: Oh, because we've rendered it too many times.

COREY: Yep. In our test real quick, do an after each block. Then call cleanup. I think that comes from React testing library, which will clean our dom for us. Perfect. All right. Let's see the test output again. Okay, one failed.

JASON: One failure. Expect the item to be

COREY: Perfect. Okay. So we passed the test that was failing, and now we failed the test that was passing.

JASON: It's a controlled input now.

COREY: Now we have to write the implementation logic.

JASON: Okay, okay. So our logic then is going to be we need like an on click.

COREY: Exactly.

JASON: So we'll do an on click. That's going to get the event. Do we need the event? Yeah, because we need a target.

COREY: Yep.

JASON: So then we're going to get the what's the easiest way to do this? Are you a form data person? Are you an event target dot

COREY: For this, I'm event target.

JASON: Okay. So we're going to say let's see. New item equals event dot target dot value.

COREY: Yep.

JASON: Then we're going to say

COREY: So now we need an action to fire off.

JASON: Okay. So that means we need to get can we just pass that out as like a helper function?

COREY: Yeah, I believe so.

JASON: Okay.

COREY: Oh, wait. I don't know if we can because we're going to need to set that state. Keep doing what you're doing, and let's see how it goes.

JASON: Oh, that's not going to work, right? You're right, you're right. We need to write a function, and that function is going to update the state.

COREY: Exactly.

JASON: So that's going to be we'll make the function called

COREY: Mark as eaten.

JASON: Mark as eaten. That's going to take the name, which will be a string. Then that is going to call await service dot where's my

COREY: We got to bring that thing up out of the use effect. I would wrap that in a use memo, too. It's just one of those things I do by default now.

JASON: Okay. Wrap this particularly?

COREY: Yeah.

JASON: You know what, I don't really use use memo. Is it a call back?

COREY: Yeah, I think you can do that. Then do an empty array as the dependency array. That will just cache this for us to we're not creating a new menu service every time we re render.

JASON: Got it. So then we're going to pass in mark as eaten, which means down here we're going to get it out. Then when we get an on click, we're going to mark as eaten the new item, and that should do the thing. So then down here, we're going to add an on click that is on click.

COREY: Perfect.

JASON: Probably name that better. You, stop helping me.

COREY: (Laughter). Cool. And what do our tests look like?

JASON: So, that should have worked, and it didn't. And it didn't because why?

COREY: Oh, because we haven't we have to re query our eaten items and set state again. So, scroll on up. We mark it on line 23 as eaten.

JASON: Oh. Right.

COREY: Now we need to ask our service can I have my eaten items back so I can set React state.

JASON: Okay. So we're going to do that quite literally like this.

COREY: Yep.

JASON: Okay.

COREY: And I think we might have to rewrap it. Remember how I said I always get got by object references.

JASON: Yeah.

COREY: I think we're going to have to do the React thing and create a brand new set again so it calls the I'm blanking on the name the reconciliation.

JASON: Like new set and spread it in?

COREY: Yeah, exactly. Do we have to spread it, or can we just do new set?

JASON: Such a fabulous question.

COREY: Let's see. What happens if you get rid of the spread? Does our test pass?

JASON: What? Why?

COREY: I guess it can take a set.

JASON: You know what, it doesn't matter.

COREY: I know it takes an array for sure. We could probably do array from and pass it.

JASON: Oh, because I would need to yeah, yeah, yeah. I needed to put the spread in an array. Because otherwise, I was just passing in a bunch of parameters. Okay. Yep. So I was just being bad at JavaScript. That's fine. Mystery solved. (Laughter)

COREY: Cool. So we have this thing working. Let's load it up in the browser and just test it real quick.

JASON: Okay. So we've got it oh. Wait, did you see that?

COREY: No, what happened?

JASON: Oh, we've got duplicates in here.

COREY: I imagine this is because we I think the menu has duplicates of, like, specials and entrees.

JASON: Oh, like it shows up in different places. Okay.

COREY: So I think this is right. And I would say this is probably the functionality that we'd want. Minus the fact that nothing is on a new line.

JASON: Sure, sure. Yeah, we could probably make that better.

COREY: But the fact that I think a menu will have specials and have the same item that's on a happy hour menu. I think once you've eaten one item, you've eaten it everywhere. Like there's no difference between the happy hour cheesecake.

JASON: That way we can at least see what's going on here. Okay.

COREY: I feel like you were going to say something. I didn't mean to step on your toes.

JASON: No, no. I was just thinking about what we would do next here because the thing I noticed when I was seeing the multiple checks was that when I do this, I can't mark it as uneaten now if I misclick. So we don't have the toggle. Do you care about that or want to move on to something else?

COREY: Let's talk about refactoring real quick.

JASON: Okay.

COREY: I feel like you were going some place, which is we were looking at that hook and you're like do I just set state here, create another state. I think you were going to say like do I merge it into our existing data set.

JASON: That's what I was thinking. I was thinking that this code looks like it would benefit from being in my mind, what I'm imagining is that we would pass out the items as an object that have a name and a status or like a flag for whether or not they're eaten so that way you've kind of got like one thing. Then we could even potentially hook the mark as eaten on to the items, which that might not be worth doing. Then you're kind of in this whole we're passing out like one thing. Then as we loop, we can do stuff on that one thing in a way that feels kind of related to it.

COREY: Yep, exactly. I think that's a great point. If we were pairing together at an actual company doing stuff, I'd be like yeah let's take the time to make that refactor. The good thing about I think the thing that I want to call out is we make that refactor, we change everything exactly how you described. It becomes this view model thing of being here's my item. It has if I'm checked or not, and it also has the action to mark it as eaten or uneaten. And our tests on the left don't have to change at all.

JASON: I always forget how these work, and they're always so irritating.

COREY: Is it HTML event? I don't know.

JASON: Of course it's not. You know what, I don't care.

COREY: Unknown.

JASON: I'm just going leave it. Yeah, why not. Oh, now you're mad? No, okay. We'll just let you be mad. Sorry. You were going somewhere before.

COREY: I just made the best point ever. No. Yes, I think the point that you made of putting this all together in a view model type thing would clean up this code, would make it way more understandable, would reduce the complexity in it. It would be the right move. The best part about that is if we did that, I promise you our tests wouldn't have to change at all. Our tests would be completely decoupled to any way you want to structure your components right now.

JASON: Right. And that is something that I've noticed that I really like. Nothing here is the only thing that assumes we're even using React is this render. Everything else that we're doing is just like is there an item that has this thing, can I click on it. How we package that up, whether or not we're using Vue or React or whatever doesn't really matter here because all we're doing is looking for HTML on the screen.

COREY: Yep.

JASON: So this could be testing like web flow.

COREY: Exactly.

JASON: Which is and I do really like that approach to writing tests because once of the things I remember doing when I was early on in my career is I would try to test I'd get 100% test coverage, but I would do that by testing implementation details. Then I would find every single time where you factored the code, I had to refactor my tests. I was like, this doesn't feel worth it. Why am I doing this? Which then led me to swing too far where I was like tests aren't as important as you think. Then I realized it was because I was writing bad tests. So a little bit of a long walk to get to somewhere healthy.

COREY: Yeah, but I think this becomes my favorite part. I get to use on your right side panel menu view, I get to use it as a playground. I get to wrap things up into a view model. Or I can bring things out into their own separate hooks. I can bring in some reactive state management library. I can make all those changes and have the confidence that they are working as intended because of the panel that's on the left.

JASON: Right.

COREY: But for the sake of this stream, let's keep trucking down the road.

JASON: Got it.

COREY: So, everything works. We could either quickly do the uncheck, if you want, or we can go up another level to playwright and do a browser test.

JASON: Let's do a browser test because we'd have to modify that service to allow removing eaten items. I think that's

COREY: Yeah, that would require another test too.

JASON: Right. So let's not bother with that. Let's instead get into playwright.

COREY: Okay. There should be an e2e folder, if you scroll up. And that's I think that's just an example file that they give you. We can rename that to menu or index. I don't know. One of the things I'm worst at is naming things correctly. So menu spec sounds good.

JASON: That tracks. Like yeah, if we're not bad at naming it, you know, then we're kind of betraying the meme.

COREY: Do I like name it after the route? Do I name it after the domain? I don't know.

JASON: Sure. Yeah, and I don't know that I have strong opinions on that one. I think I agree that menu is the right call in this case because we're writing the menu lib, and everything we're doing is wrapped up in that. So naming it after what we're testing seems reasonable.

COREY: Perfect. I'll follow your lead and say Jason says we're doing great.

JASON: (Laughter)

COREY: I feel safe. Okay. So this has like a test file oh, my gosh, an example test for us. Let's go off of this and let's just change the description. Let's do like the entire behavior of I show up on the page, I see a menu item, I check it, it's now checked. So let's how do we incapsulate that into a describe block?

JASON: View and mark item as eaten, right?

COREY: Perfect, yep. Okay. So we go to the homepage. We should be able to use like the same find by label. So I think it should be page dot find by label.

JASON: Okay. So item is going to be page dot find get by label?

COREY: Yeah, sorry. That's exactly what it is.

JASON: So we're going to grab cheesecake again. The end to end, does this use the mocks?

COREY: So this right now is using let's hop down real quick to the menu service file. And we have this get menu service. This is using our cheesecake factory menu client and our in items fake. So this theoretically should work.

JASON: Okay. So I'm going to use this then.

COREY: But we don't have to set up any code.

JASON: We oh, right. Because it's going to load the actual page.

COREY: Yep, it's going to load the actual browser.

JASON: Got it. But that does mean it's not going to be using our fake labels. It's going to be using real ones.

COREY: Yes.

JASON: So I should pick one of these.

COREY: Great call. Man, you are so much smarter than I am. I would have been like why isn't cheesecake in there?

JASON: (Laughter)

COREY: Why isn't cheesy cheesecake showing up in our browser? Okay. So we get the item. Let's just do oh, my gosh. Let's assert that it's not clicked yet. Let's like run this thing soup to nuts. So right now we're finding an item by the title.

JASON: Not to be checked.

COREY: Exactly.

JASON: Okay.

COREY: Then we want to

JASON: Should we start here and make sure this is right? Is it test e2e? Is that how we did it?

COREY: I believe so.

JASON: Okay. So that failed because executable doesn't exist. What am I missing?

COREY: Do that npx playwright install.

JASON: Oh, boy.

COREY: It's possible we haven't installed.

JASON: It is entirely possible. I don't know if I've ever installed playwright. Trying to think if I feel like the last time I was doing the end to end stuff, I was using Cypress. Let's see. So we've got it now. Let's try again. Okay. View item, mark as eaten. It says

COREY: Not checked.

JASON: Oh, not to be checked. No, it should be not checked.

COREY: Yeah.

JASON: What are you mad about?

COREY: Oh, it needs to be awaited. Sorry. Playwright world.

JASON: Like this?

COREY: Yep.

JASON: Okay. How do I

COREY: I don't know. Let's just cancel out of this and rerun it again. I don't think it does

JASON: Is it because I'm getting the label? Strict mode violation. Get by label resolved to two elements.

COREY: Okay. Let's get something that doesn't have two elements.

JASON: So we're going to find maybe something down here. I don't know. We'll keep trying at random until one of them doesn't have two instances. Or alternatively, we could just dedupe the list.

COREY: Yeah, that also works.

JASON: If this one gives us okay. So this one passed.

COREY: That's great. Okay. So now we want to click it.

JASON: Let me just make this a little uh, so we're going to do we await this as well?

COREY: Yeah, I think in Playwright we await everything.

JASON: Okay. Then we would await expect item to be checked. Okay. Theoretically speaking, that's just going to run and work. And it does.

COREY: Okay. Perfect. So this is passing. I also do you mind opening up package JSON real quick? There is the e2e UI. You're like, how do I redo this? I forgot this is the way I usually do it. It gives you a nice UI you can hit that play button, and it'll play through all your tests.

JASON: Oh, this is just like Cypress.

COREY: Exactly. So right now, this is using a fake storage layer. How would we get this test to fail? I don't mean to act like your teacher being like Mr. Jason, how do we make this fail.

JASON: Uh, no, I appreciate it. So, how do we make this fail? We would make this fail by refreshing the page and realizing that it didn't persist.

COREY: You are so smart. A+.

JASON: I would like my gold star now, please.

COREY: You got it. So let's do await page reload.

JASON: Okay. So we reload the page.

COREY: I think we just have to do line 12 again. Oh, sorry. We have to query.

JASON: So it would be like this, right?

COREY: Yep.

JASON: I'll do it like this. Okay. So now this should fail.

COREY: Yep.

JASON: And just run it again. It reloads.

COREY: And fails. What's the error say?

JASON: Expect to be checked, which it's saying it's not. We can see down somewhere in here, if we can find it, that it's not there.

COREY: Awesome. So this is us kind of driving to the right implementation. I feel like this is also something check out the big brains on Jason. (Laughter)

JASON: Thank you.

COREY: Like, this is something I try to do intentionally, which is not reach for the solution that I'm going to need and just reach for the solution that's going to pass the test for now. Then I come up with the next test that's going to fail. I think through this maybe I'm just full of crap, but I think you end up with better decision making after you know the problem.

JASON: So just to repeat that back and make sure that I understand where you're coming from, what you're saying is if I'm building this app, my non test driven approach would be to say like, oh, I know I'm going to end up using a database, so let me start by installing SQL lite or whatever the thing is that I'm going to use. Then as I start implementing that, all of the UI decisions, all of the service decisions are kind of all lumped together, and I'm not really thinking about them as distinct things that might swap out over time. I'm thinking of all of them as an extension of my database.

COREY: Exactly. Yeah, check out the big brain on Jason. You just verbalized exactly the gobbledygook I have in my brain that I can't articulate very well.

JASON: But yeah, so having lived through this pain a lot, because I love prototyping, and my typical approach is to run through it in a white board way. Once I've figured out the services, I start smashing service together to get something that mostly functions. But then almost always like when I was at Netlify, for example, I would prototype things, but when I brought them to other people, I said the first thing you should do is completely destroy this and build is the right way. I'm showing you it's possible. I'm not showing you how it should be built.

COREY: Yep, exactly.

JASON: Because I always made some kind of a rat's nest. What I like about this is that I haven't looked a the this code since December when we worked on it. I have yet to step on my own code in this project, which is really interesting. I don't think that's ever been true before.

COREY: We'll just pair together constantly. And to your point of prototyping, I rely on this stuff at work a lot. I do a lot of like user facing stuff where I'm talking to users, really understanding their problems, and trying to put good solutions in front of them. I will the state that this application is in right now, I would deploy up to some type of staging environment, go to a user and be like, hey, can you click around this and just think out loud? That way I'm not tied to a database. If they're like oh, I need these menu items to load instantaneously, like these need to load in 16 milliseconds, that's going to change the way I go about my implementation.

JASON: Right.

COREY: Versus them being like I have ten minutes to wait to get these items. Like, that would be like, now we have the time to dedupe our list. Or we have time to go off and fetch more data around calories and stuff like that. Putting in what I call a walking skeleton in front of a user with the not final implementation, you get a lot of good feedback, and you also end up in a world where it's like not just crappy prototype code, but it's actually something that can grow and expand all the time.

JASON: Yeah, no, I like this. Okay. Okay. So now we have an actual need. We've hit the point in our testing where we need to persist between page loads.

COREY: Yep.

JASON: What do you want to use for this?

COREY: Do we need a database?

JASON: I mean, we could use local storage, but that makes some assumptions about how somebody might use this.

COREY: Yeah.

JASON: Because I know personally, I'm probably going to use it across two devices. I'd have my phone at the Cheesecake Factory and check at home for my progress.

COREY: Yep, but for the sake of this test, just to get the test to pass, let's do local storage. Then I would deploy it and say Jason, look at this. You'd be like I want to check this off at the Cheesecake Factory and I want to go back to my computer. Then I'd be like, okay, let's bring in a database. For now, let's go to local storage.

JASON: Got it. Okay. For us to do that, I'm going to when we get into our eaten items service, which is so we have our eaten items store. Then we have fake. So is this going to be our real one?

COREY: Well, I don't know what's real. What is life? I would call this like eaten items local storage.

JASON: Okay.

COREY: And this happens to me all the time. I'll have different implementations. Like, I'll have a menu service rest API or a menu service GraphQL. I don't know. I'm just trying to come up with examples. I try to keep things specific, and I only get rid of things when I know I don't need them anymore. So I wouldn't name something real or the final.

JASON: Fair, yeah. Final final final dot VT.

COREY: Exactly.

JASON: So if we're going to do this, we should probably do it as a serialized JSON. Just a list of names. So we're going to get item, and it's going to be eaten items. Do you have any preferences on like naming conventions and stuff?

COREY: I have no preferences ever. Whatever feels good.

JASON: Right. So what I've taken to doing is just putting some kind of an identifier on it so that it's somewhat clear what the hell is going on.

COREY: Yeah.

JASON: But I don't know that it really matters. So then what are you mad about? Oh, I haven't implemented methods yet. So this is not quite correct because we need to JSON parse. And you're mad because

COREY: Oh, we'll have to let's just create our methods to implement the interface.

JASON: Sure. So we need

COREY: We need get eaten items and mark items as eaten.

JASON: Okay. So get eaten items is going to be one of these. And that oh, that's going to be

COREY: Yeah, that way you can just do an old check in there.

JASON: Okay. So this then is going to just be our new set.

COREY: Yeah, we can do that.

JASON: Then this is going to be let's see. Stored will be local storage. And then down here, we can see if stored, then we can just return.

COREY: We have to return empty items to adhere to the contract. Yep, exactly.

JASON: Okay. And otherwise, we're going to return JSON oh, that's my name parse. Stored. Okay.

COREY: I think that also has to it's going to be an array, so I think you have to wrap that in a new set.

JASON: Yes, that is absolutely correct. Okay.

COREY: Then I think we can probably get rid of items because we're not using that. Up on line 32.

JASON: Wait. Oh, you're right. You're right. Yeah, okay. Well, hold on.

COREY: I'm holding.

JASON: Because we are going to need to serialize these to put them into local storage. So won't we need to be able to share them between?

COREY: Maybe.

JASON: When you mark an item as eaten, that's going to update well... yeah.

COREY: Yeah, let's do it. Do whatever you want. We have a test.

JASON: So my thinking here is we've got this, and this is going to be async. This one takes a name. Then it is going to so this needs to update the item. So it would need to have access to the items because it's only getting the name.

COREY: Yep.

JASON: So we would need to be able to do something like items dot add. So yeah, we need that, I think.

COREY: I would argue, and I think we're both very smart individuals. I would call this dot get eaten item.

JASON: Wait, why did I oh, I spelled it wrong is what happened. I was like, what is going on here? What are you mad about?

COREY: I think this needs to be async too.

JASON: Not assignable to the same property, set unknown.

COREY: Oh, we have to put a string in between the pointer boys, as you would say.

JASON: Oh, here.

COREY: Yep.

JASON: Okay. Like that? Now you're happy. Okay. So you're saying instead, do this dot get eaten items. We're going to say

COREY: Items, yep.

JASON: Come on. And then we're going to say items dot add name.

COREY: Yep. Then toss that back in.

JASON: What does this return?

COREY: A Boolean. So just return true.

JASON: So then we're going to local storage set item. And this should probably be in a variable of some sort. And we're going to set that to JSON stringify items. And to do that, we need to probably spread it into an array. Right?

COREY: Is that right?

JASON: I think that's the easiest thing to do. We could do it a handful of ways.

COREY: Yeah, that looks right to me.

JASON: Okay. Then we're going to return

COREY: Perfect.

JASON: So that should actually persist. Then when we get our eaten items, those will show up, which means this is all going to work. That means all we need to do now is swap out our it was down here to use the local storage instead of the other one. And we don't use that anywhere else, right, except in the tests?

COREY: The local storage?

JASON: The fake eaten items.

COREY: Oh, yeah. The fake we only use in the tests.

JASON: And we don't need to change that one. That one we're not trying to check persistence.

COREY: Yep.

JASON: So the moment of truth here then is I should be able to check chicken shawarma and refresh the page. So my test, my manual testing worked. Now let's try our real test and see what happens. Oh, and I can just go back to my UI, run it again. Oh, no. What happened?

COREY: It passed, right?

JASON: Wait, it did?

COREY: There's a green check mark on the side of it. I don't know what those red Xs are. What happens if you run this in non UI mode?

JASON: I'll just refresh the entire thing and try it one more time. It says it passed. Is that because you think it's slow?

COREY: That's possible.

JASON: Well, no, but that one was really fast. I don't know. Okay. So let's run this. That passed. Okay. So we've got persistence.

COREY: We got persistence.

JASON: We have tests to prove it.

COREY: We have a test to prove it, and it's testing through the browser. And this is where things get really fun and interesting. We have 15 minutes left. I don't know if we're going to get to it, but I would say let's role play that I'm your manager. I'm like Jason, this cheesecake I don't know why I'm trying to act all noble as a manager. Jason, this Cheesecake Factory menu service you built, it's crushing it. The numbers are through the roof. I'm going to drop this voice. I'm just going to act like myself now. The Cheesecake Factory menu service is doing great. I listened to some podcast, and they were talking about Astro and serverside rendering. It sounds really interesting. I think we'd get a lot of benefit from it in our cheesecake menu service. Can you put together like a little prototype or walking skeleton of doing this in Astro land?

JASON: Yeah, I think we can really fast, actually.

COREY: Okay.

JASON: So for us to do that, we've got our index here. Right now we're pulling in the menu view.

COREY: Yep.

JASON: But I believe this is all very portable. Like, none of this is React dependent.

COREY: Right.

JASON: So the thing I would need to do then, is I would need to load in our menu view. No, I would need to load in our why don't we do this. Let's make a component, and we're going to call this menu view dot Astro. Then I think we can do almost exactly the same things, right?

COREY: I think so too.

JASON: So this is going to oh, hold on.

COREY: Oh, snap. (Laughter)

JASON: Look at us go, everybody. Okay. So then we've got our so our menu view is in here. I'm going to grab out this stuff, which we should be able to drop right there. And then I'm going to where is it? Get menu service gives us the whole service, and we shouldn't have to worry about const service equals get menu service. Then let's just start by doing my favorite thing, which is to JSON dump everything.

COREY: Okay, cool.

JASON: And I'm going to just service dot get items.

COREY: That might be a promise. I actually think that is a promise.

JASON: Oh, good call.

COREY: Let's just await that up in the front matter.

JASON: Okay. So now we can do this. Out here, instead of using this, we can then pull in comment this one out and say import menu view from lib menu components. And we'll get the Astro one. Then we get all of our stuff. So we're already in good shape there. So let's go grab our logic here. And we can actually take this whole thing and do one of those. Then we don't need keys anymore because it's not React. This isn't going to do what we want, so we're going to leave that out for a second. Then we don't have our eaten items yet, but we sure can.

COREY: Yep.

JASON: Okay. So then theoretically speaking, we should see

COREY: Ah, wait.

JASON: You have got to get out of here. Local storage is not defined. Oh.

COREY: Yeah, sorry. I set you up for failure. Let's drop in our fake again.

JASON: Okay.

COREY: And if we were in a real world situation, this is where I'd be like, oh, I got to reach for a database. Or I would go to my fake and mark it down, and when I'm presenting this to the team, I'd be like this is what this looks like in Astro. We'd have to bring in a real database.

JASON: So now we have like right now, they're uncontrolled inputs because we dropped out our click handler.

COREY: And if we ran our end to end test, I'm guessing things would break.

JASON: It would fail, and the reason it would fail is because our persistence isn't working.

COREY: Totally.

JASON: For us to fix that, we would we could swatch to SQL lite really fast, and that would probably solve this problem. I don't know that we can do that in five minutes or whatever.

COREY: I think we can probably continue to rely on this fake.

JASON: Okay.

COREY: Let's just to make sure I'm not going crazy, let's pull this fake out so that way when we call get servicemen you, we're not creating a new one. Like, let's just save this all to its own variable under the scope of this method. Because this is running on the server.

JASON: Right.

COREY: So this should become a singleton.

JASON: Ohh, so it would work then.

COREY: It would work.

JASON: Uh, maybe. We'll see. Because Astro might be running more like serverless, in which case the single doesn't help us anymore.

COREY: Yeah, totally.

JASON: I don't know what it's going to do in dev mode, though, so let's find out.

COREY: Okay, but we haven't actually written to the store yet.

JASON: Oh, you're right. We haven't. So here then what we can do is we're going to what's the fastest way to do this?

COREY: My fastest way would be to write JavaScript.

JASON: Yeah, so we could do it JavaScript side. However, actually, because we've moved this to a serverside thing, I don't know how that would work. So I think instead what we want to do is shoot. So Astro has got actions.

COREY: We can do an action.

JASON: But we would need to turn each of these into its own form we could submit, which is fine. That's easy enough. Form method. We'll make it a post. And that's probably fine. Actually, what we can do is if Astro dot what is it?

COREY: Request.

JASON: Dot method equals post, then what we'll do is we can say if data equals new form data, Astro request dot body. I think that will work. No, it's not going to work. That's a whole buffer, isn't it?

COREY: Can you do form data? Request dot form data. Is that a thing?

JASON: Oh, yeah, do they just give it to me? They do. Then I don't actually have to wrap it. That's right. Good job, Astro, for making that easy. Then we can get the item equals data dot get no, data dot get name.

COREY: Yep.

JASON: Why are you doing that?

COREY: Oh, I think it has to be awaited.

JASON: Okay. Then we can service dot mark item as eaten item.

COREY: Yep.

JASON: That should solve that problem. We're just going to cast a string because why not.

COREY: Do we have to do a name equals name on line 20?

JASON: What?

COREY: Get name. Does that mean

JASON: Oh, right, right, right. We need to give it a name.

COREY: Sorry, my brain short circuited for a minute. I said something I thought made sense.

JASON: So the other thing we have to do, though, is now I'm going to do this the really gross way, and we're going to

COREY: Submit it.

JASON: Move this up here and add a button type submit. Because I feel like what I want to see is that we just refactored this to being serverside and it works. And I don't care if it looks nice. No! Content was not one of

COREY: Oh, I've seen this. I think we have to do a pre render equals false.

JASON: Oh, am I in hybrid mode?

COREY: Yeah.

JASON: Re render equals false. How dare you.

COREY: Seriously.

JASON: Astro form submit form data. And I'm missing what? So here's our form.

COREY: Yep.

JASON: Method post, yep. Then we get our form data.

COREY: Yep.

JASON: Boo. What's going on? What's our let me look real quick just to see. So we should be in hybrid mode by default. Let me just double check that.

COREY: Okay.

JASON: Let's just make it server mode. Why not? That should solve this problem. So we're going to do that.

COREY: Oh.

JASON: Now when I I don't want to refresh the page. I just want to load it again. And look at us go, everybody.

COREY: All right. The moment of truth. Run that end to end test.

JASON: Okay. So we're here. End to end test. Come on.

COREY: Oh, this is not promising.

JASON: No! What changed? Timed out?

COREY: Oh. Because you have to hit the save button.

JASON: Ah, dang it. But that's okay. We can fix that. So I guess

COREY: Okay. Let's go to your component. I have a way.

JASON: You just want to make it click?

COREY: Yeah.

JASON: Gross.

COREY: I know.

JASON: What is it in Astro?

COREY: I know it on the input to do on change.

JASON: Got it. On change.

COREY: Then in a string, it's like this dot form dot submit.

JASON: In a string?

COREY: In a string. I think this is a browser thing.

JASON: With the braces?

COREY: I don't know. Let's do no braces.

JASON: Are you mad because I capitalized something? There we go.

COREY: What happens if you refresh? Okay. Try the parens.

JASON: Is it doing anything?

COREY: That's a great question.

JASON: Look at it go. Here we are. That's a submit. So let's run this again. So we just re I mean, we made a mess out of this.

COREY: Yes, definitely.

JASON: But we just refactored to serverside rendering, and we didn't have to change the tests.

COREY: Didn't have to change the tests, didn't have to change our menu service.

JASON: That's that's freaking huge.

COREY: I know.

JASON: Like, the I think the thing that always made me hesitant about tests was the risk that, like, we would get down this road and get some request, that request requires to refactor something, and that refactor would inevitably break 100% of our code. Then it was like, all right, are we ever going to get the prioritization to be able to, like, rewrite tests? And the answer was always no.

COREY: Right.

JASON: But if we're changing functionality and the tests we write only exist to prove that the functionality we've designed works, now they're part of the feature. Right? It's no longer like a nice thing that we're doing to make a point about like, oh, look how much test coverage I have. It's like no, this is actually what proves that our app works.

COREY: Yeah, your ability to articulate this is amazing. I think that's like the most spot on way to describe this.

JASON: It is my gift of spending 15 years explaining this kind of stuff. (Laughter)

COREY: Yeah, but for me, it takes me two pairing sessions to actually like show the benefit.

JASON: Well, I mean, the thing is I don't know that I would have been able to do this without the guidance. So, you know, my gift is always summarization. So I think that this is one of those things that it helps it kind of click into my brain why this is valuable and how much of a benefit it adds to think about your code in this way and break it apart. Because I think that I and a handful of people in the chat, when we first saw that we were making a menu service, man, this feels like a lot. Well, here we are later, we just refactored the entire app to do a completely different rendering mode. We dropped React. Huge migration. And none of the tests had to change.

COREY: The surface area of the code you had to change was just the technology that changed.

JASON: Just the view layer. Like our service didn't change. Our test didn't change. Only the view changed. That's really powerful stuff. So I think that's about as much time as we have. For folks who want to take it further, where should they go if they want to learn more? Let me start dropping links to your stuff here. Is there anything you would recommend for people to check out?

COREY: I think just like most presentations on TDD will cover this stuff. This bookshelf is most of my wife's that reads a lot of romance novels, but there's one book in here I come back to on a yearly basis. It's "Growing Object Oriented Software Guided by Tests." GOOSE is what people call it. It's in Java. We have ChatGPT now where you can type in Java code and give me the JavaScript version. But I re read that at least once a year. It's informed a lot of my mentality around writing software.

JASON: Yeah. Well, this is great. So, yeah, let me just really quickly commit everything that we did. Git add everything here. Then just git commit and say more features, more tests, all passing because we are geniuses. Then we're going to get that pushed. If you want to see this code here, this is the repo we've been working on. And you can go kind of follow along, check that out, give it a try, get in there and write your own tests, make some changes, see how they feel. But this was really nice to work in. Corey, thank you so much for taking the time to teach us this stuff because I really do think this is the kind of stuff that makes teams function, not just right out of the gate but for extended periods of time.

COREY: Totally. Yeah, go fast forever is the phrase. It's very gimmicky but great.

JASON: Go fast forever is an excellent phrase. I love that. Any final words before we wrap it up?

COREY: Uh... I guess not. Thank you for watching. I mean, it's hard to sit through, I think, three hours worth of coding. So people who are doing that, that's awesome. It's something I like to do in my spare time. So, I appreciate the attention.

JASON: Excellent. All right. Well, Corey, thank you so much. Y'all, thank you so much for hanging out. Thank you to Rachel from White Coat Captioning for doing live captioning today. We'll see you all soon. My schedule is empty right now. So if you got somebody you want to see on the show, let me know. I'm going to be reaching out to some people as well. But we'll be back. Keep an eye on codeTV.dev for updates. Thanks, Corey. Thanks, y'all. See you next time.

Supporters

Become a supporter to see yourself here on future episodes!