Florent Biville et Agathe Vaisse
Duration:
Views: 394
5 likes
Published: November 4, 2020

Transcript (Translated)

[00:00:15] Uh, hello everyone. Thank you for being here. Tonight we're going to present development hygiene in times of pandemic, that is, we're going to talk about two working methods and two pandas. So, I'm Agathe Bess, I'm mainly a back-end developer, exclusively at WSMF. Uh, developer wasn't my first job, I retrained a few years ago. And, uh, I co-organize with Florent, the Meetup Hacker Garden Paris which takes place every month, and uh, hack committee push which takes place once a year and which, uh, in fact, these are events that allow contributing to open source projects. Uh, besides that, I'm also passionate about fantastic literature and tonight, it's my first talk, it's my first live coding, I'm on a Mac with a QWERTY keyboard, so suffice to say that my comfort zone is very, very far away. So thank you for your kindness and I'll let Florent present.
[00:01:09] Uh, yes, so effectively, this will be a live coding with all the risks it may entail. Uh, for my part, I've been a developer at New Forge for a little over a month. I was at Pivotal which was acquired by VMware afterward, so I stayed with VMware for a bit. Um, so like Agathe, I also co-organize the Meetup Paris every month, even if this year the period is a bit complicated. The Hacker Garton Paris to bring together open source project maintainers and potential contributors who want to be a bit guided on their first contribution. So we do that every month, for that matter, especially virtually right now. And Hack Commit Push that Agathe also mentioned, which is the same format as Hacker Garton, but with more people and longer throughout the day, which is free and which is for now once a year in Paris and if the context improves, we'll probably do it elsewhere too. And my passion, apart from doing live blogs, is to learn Turkish, which is my beautiful family's language, so there you go. If there are Turkish speakers by the way, you can ask questions.
[00:02:08] Ask questions.
[00:02:09] There you go, ask the questions, it'll be a good extra dose of stress.
[00:02:15] And so, what we're going to show you today and maybe convince you of, is an example, it's our way, at least when Agathe and I work together, of doing Test Driven Development and Pair Programming. And for that, we took the bias of a small, quite simple application, a small web game that we'll show you a little later. Again, you shouldn't take everything we do literally, it's our interpretation, we may have strayed a bit sometimes from more practices. So make your own idea, we're just here to illustrate that it's not something reserved for an elite or whatever, these are methods that are open to everyone, whatever the codebase you're working on. Especially for beginners. Yes, completely.
[00:03:01] So Test Driven Development, what is it? Test Driven Development is above all a cycle, in fact, a cycle that is centered on feedback. We want immediate feedback. And what we're going to do very often is that we're going to express our intention, so most often in the form of an executable test, we're going to write a test before, so we're going to try to express our intention in the form of a test. Obviously, the test, since we write it before writing any implementation, it will first not pass, it will first be in failure. So there is this notion of red. that will arrive right away. So what we want to see first is express our intention, verify that there is nothing in the code that is ready for this intention to be already realized. So that's the red step. Then, strive to ensure that the test, so our intention that was translated into code, passes. And so that's the green step because then the test will pass, it will be successful, and so really do the minimum possible for the test to pass. So we're not going to try to code an entire application in that step. We're really going to think about a particular aspect of the feature we're developing. And once it passes, Well, then we can take a step back. We say, okay, so there, okay, I just did the minimum, that's great. Can I improve what I just did a little? Oh, there's duplication, for example, we're doing the same thing in two or three different places, maybe I can centralize that, etcetera, etcetera. So there you go, it's not just, it's not just the blinkers there. We try, we take a moment, but not all the time, at a very precise moment, when all the tests pass, we can afford to reflect on and improve the things we've written. But in any case, it's really focused on the fact that we have a test in failure. Then, a test that we make pass via minimal steps. So you'll sometimes hear about baby steps because we're really trying to do the minimal step. And finally, refactor. So refactoring has a very specific meaning, it's to modify code so that it's more maintainable. without however modifying its observable behavior. Otherwise it's no longer refactoring, it's a modification that potentially causes regressions. So that's not something we want. And so, what you also need to see, and we don't necessarily realize it at the beginning, we realize it gradually with experience, is that Test Driven Development is also an acceptance that, uh, we don't know everything. And even when we think we know everything and we think we can implement everything at once, finally, we always make mistakes. So it's also about staying humble and, compared to the unknown that unfolds before us, and so it's really the idea of forcing yourself. So at the beginning, you have to force yourself. It's not necessarily natural to advance step by step with these famous baby steps, exactly. So there's really this notion of humility in saying, I'm going to force myself. Even if I think I have a precise idea or not of what I want to do, I'm going to force myself to go step by step and, uh, and advance little by little. And meanwhile, there are a lot of positive side effects of Test Driven Development. One of them is that since we force ourselves to declare our intention before implementing anything, so we're going to implement our intention in the form of a test. Well, that also allows for having an objective communication support. Because for example, if you work with someone else or in a team, uh, if someone comes to know what you're doing, you say, well, I'm working on this test. And that's completely objective because it's a test that will pass or not pass, so someone else can read it and understand what we're trying to do. So it also helps a lot with communication. And finally, this is the Captain Obvious part, but when you do TDD, you necessarily have testable code since it's tested. So that's a bit logical, but it's not necessarily the most important aspect. the most important, it's a very nice, very practical benefit, which allows you to have a whole bunch of non-regression tests afterwards. But really, I see TDD above all as a communication support and for oneself, to know in which direction, in which direction we are going, but also for others, to then bring them on board, especially when we do pair programming or mob programming.
[00:06:55] So what is pair programming?
[00:06:58] So pair programming is a development practice that we practice in pairs. With a pair. It can be co-located, so we'll both be in the same place, on the same workstation as we are tonight. But it can also be remote, which is what we practice a lot currently, with screen sharing. So in the pair, there will be a driver, a pilot, who will code, and a navigator or co-pilot who, uh, who will do research, who will check things, who will perhaps contradict what was coded by the pilot, or who will give their opinion, simply, who will try to have a global vision, in fact, of the code that is being written and who will also maintain the global vision of the application. So that's his role. Uh, there are several ways to practice pair programming. Classic, as I just described, in fact, it's going to be a pretty natural discussion and, uh, we're going to change roles at regular intervals. Ping-pong, so that's pair programming with TDD. So one writes a test, the other writes the implementation, the first takes back the keyboard to do the refactoring, and uh, and so on, in fact. So, really, everything is broken down between, between the pair. And strong coupling, so it's something I've tested a few times that I appreciate moderately. but you should know that it exists. So the pilot will only code what the co-pilot tells him to write. So, there's really, there's really another relationship, but well, it can work in certain cases. So, why not, especially perhaps with someone less experienced. Okay. So, these are the advantages of pair programming. Uh, we are specialists in very funny jokes.
[00:08:41] Uh, that's it. So, there you go, the advantages of pair programming. Uh, we are specialists in very funny jokes. So if you lose, you win. Uh, it will especially give us very quick feedback on our, our code, because there's someone who will constantly look at it and give their opinion on it. Uh, we'll be able to improve our skills, each one, uh, each one the other, in fact, we're going to pull each other up, we're going to learn from the other. That's really something, uh, very important in pair programming. We're also going to try to strengthen the team spirit, collaboration. So it's good in a team to rotate the pairs so that it's not always the same people who work together. Uh, if we ever hit a wall and we absolutely don't know what to do, well, we're not alone. So generally, even when things are complicated or seem complicated, we'll manage to move forward. And above all, don't forget that in pair programming, you have to take breaks, you have to take your time, it's not 9 am to 6 pm in pair programming because then it's really, really hard, you really have to accept that it's good to take a break from time to time. And, uh, that will allow for better quality of work.
[00:09:56] And it's time for live coding. Let's go.
[00:09:59] Yes, and so the mystery of the panda finally revealed. Uh, because we had, there you go, we did a huge market study, there's a very, very great need for a new game of hangman. Except that the hangman as such, the concept is a bit, a bit gross, it's a shame. So instead of exposing children to a real hangman, we thought with a panda it was cuter. So the panda, when it makes a mistake, it loses its candies one by one until it's inconsolable. And so the idea of the hangman, if you don't know it, just in case, it can happen, you have a word to guess, a mystery word to guess, and you have a limited number of tries, sorry, to guess the letters of the word. So you submit character by character. So what we've done, and what we're going to do now, is that we're going to complete, well, we're going to try to complete, if all goes well, if the demo gods are against us, it will go less well. We're going to try to complete the game precisely to implement the rules of the game of Penda. which is therefore a panda that replaces the hangman. So I don't know how to pronounce it, but I'll let you decide among yourselves.
[00:11:00] And so, in fact, what we're going to do is in ping-pong mode, as Agathe explained, ping-pong, so remember the red-green-refactor. The test, the first one writes a failing test, the second one fixes the test. introduces a new failing test, then eventually with a refactor in between, and so on. And that's how it works all the time. So, let's take stock of the application. So, what does it look like today? So there, I'm going to, I could launch the real back-end, but I'm going to launch a fake back-end. In general, we actually use a dictionary API to get a random word in different languages. There, I'm going to do the quick version, so it'll go a little faster. So I'm starting my APIs in the background, which we've implemented. We're not going to do that live, because it would take us a quarter of an hour if we wanted to do the whole API in live coding, and it wouldn't necessarily be much more interesting to illustrate our mechanisms of TDD and pair programming. So I'm waiting a bit for the app to start, I open a good browser. Okay, the app has started, so we're happy. And so, what does it look like, just to give you an idea, even though the game in this case is not completed, it's not finished, so we're going to have to complete it. So we're going to choose a language, well, it turns out that we have a lot more in reality. Let's take French for example. And we have a magnificent screen where we have to guess a mystery word. So here we realize that there are five letters to guess. And so we're going to guess them little by little. So you'll see that not much will happen there, I'm trying to guess letters, nothing will happen. Why? Because we finished the UI work. But we haven't, uh, we haven't implemented the game logic yet. So we have a question about TCR. So for once, indeed, there's Ken Beck who, who introduced this method. a few months ago or maybe a year, I don't remember exactly. I admit I personally haven't tested it. So I don't have a particular opinion on TCR. It's something that's on my to-do list, but unfortunately not yet at the top of my to-do list. But no, for once, no opinion on that from my side. I have no particular opinion on TCR. It's something that's on my to-do list but unfortunately not yet at the top of my to-do list. But no, for once, no opinion on that from my side. on my side. And so, in fact, what's also interesting when you do TDD or pairing is that when you don't know where you are anymore, you leave a test in failure. So in our case, we're going to start with what, we're going to start with an end-to-end test in failure. So we're going to see what happens, if I run my end-to-end test. And I should therefore have a failing test that will remind me a little of where I am in the code. And that can be very practical, especially in the evening, for example, when you finish your day, you say, well, I'm going to stay on a failing test, so tomorrow, I'll know where I am exactly. I know where I am exactly.
[00:13:36] I know where I am exactly.
[00:13:38] And so there, oh, patatra, there's a test that doesn't pass. What is this test? If we look a little higher, Show Display fail attempts in 30 order. So if we look a little bit, it's an Angular app, by the way. So it's based on Protractor for end-to-end tests. And so if we look, and even if you're not a full-time developer, even if you don't do TypeScript or etcetera, it should be relatively understandable what's happening here. Because we're on a Page Object pattern, we're going to write our page like we would write a classic class in object-oriented programming. And so there, typically, what's happening, I have my language selection page, which we saw earlier, I choose a language, so I choose French. Uh, I try to guess two letters, and it turns out that the two letters don't correspond to the random word that was chosen. So how do I know that? You'll tell me, but where is the random word we have to guess? Well, in fact, it uses the fake back-end that I explained earlier, and you actually see that in the French language, the word to guess is Panda. And in Panda, there's neither the letter E nor the letter B. So when I retrieve the failed attempts, I should have, in alphabetical order, B and E. So that's where we are, and that doesn't pass. So, that gives us a direction where we want to go. We already know that the failing attempts for now, it doesn't pass at all. And so, what we need to look at now, well, the page turns out, so here we're going to, we're on a slightly idealized version of TDD and pairing here because we, in fact, we've already coded the app before, so we already know more or less in which direction we want to go. In reality, it takes more time, but you have to accept that, especially at the beginning, these things take time. But here we're going to go a little fast since we have limited time. It turns out that in fact, if I look at the failed attempts, on my HTML template, so the HTML of the page that interests us, uh, we're going to display them. Exactly. And it turns out that in fact, if we look at the failed attempts today, it comes from a game state. So we take the failed attempts and we sort them by alphabetical order, by order according to the language code. And if I look at this famous game state, where does it come from? If I go back to the template, well, this is a bit of Angular technicality, but whatever, I have an observable that produces for me, so I have a kind of channel that produces game states each time. and I look at a particular game state. And if I look where this 'conduit' comes from, this observable of game state, it comes from here. Okay, great, but who initializes it? Now we'll see what happens, but for now, it's hardcoded on an observable with an empty game state. So in fact, I have a conduit that will only have one value throughout its life. So that's clearly not going to work, so we're going to have to fix that. So in fact, I have a conduit that will only have one value throughout its life. So that's clearly not going to work, so we're going to have to fix that.
[00:16:10] And the game state, in fact, it will represent the state of the game with the word to guess, the maximum number of attempts allowed, the number of remaining attempts and also to know which letters of the word have already been guessed or not.
[00:16:25] And so I'm going to speak much less because I'm going to start precisely adding, entering this red green refactor, and so it's Agathe who will comment on what I'm doing.
[00:16:34] We're going to use the notion of service, in fact, to make a bridge between this letter search and, uh, and this game state which for now is hardcoded and which will represent the state of advancement of the game. So we're going to write a first test that will initialize, in fact, an initial game state that will launch when we start the game.
[00:16:56] Yes. Yes.
[00:17:00] So we're going to call it service, sorry. So we're going to call it service, sorry.
[00:17:04] Yes, service.
[00:17:07] So we're going to subscribe, we're going to use an initialization method that we're simply going to call init.
[00:17:15] The word to guess will therefore be Panda. So the two arguments of our init method will be the word to guess and, uh, we're slightly anticipating, but we already know that we're going to follow a pattern of characters, in fact. So we're anticipating a bit.
[00:17:34] And so there you see that we are not on TDD to the letter because I'm cheating a bit, I know I'll need it later. In reality, I could have just settled for the first parameter there as Agathe said.
[00:17:44] What will happen next is that we will, ah yes, just one second, game state with a dollar. So the dollar, in fact, is an Angular convention that is widely adopted, which indicates that the game state constant there is an observable. So we generally use it for that. So we use it generally. And we're going to subscribe to this observable. Uh, and by starting to play, we'll be able to receive a brand new initial game state. There you go. And we expect that, in the assertions, we'll expect that the game state has a remaining number of attempts. equal to the maximum number of attempts the player is entitled to, and, uh, we also expect that the number of failed attempts is equal to zero. Because for now, there have been no attempts from the player to guess it. So the 'done' at the end is just to indicate that we're going to stop there, in fact, because our test is asynchronous and, uh, we need to signal that the test is finished when we've received the observable.
[00:18:44] So some TDD adherents stop at compilation errors. They say, well, it doesn't compile, it returns a red state, which is debatable. For my part, I still like it to compile. So we're in TypeScript, we're not in JavaScript, so there's effectively a kind of transpilation or compilation phase, call it what you want. Uh, I still like it to compile at least minimally. So, I still put the signature and it also helps to reinforce the intention of the test, actually. We can see very well what needs to be done. So I do the minimum thing for it to pass. Now, that's debatable, it's debatable, there are really plenty of ways to do it, again, it's our mechanism, it's not necessarily the one to adopt universally. And so, at this stage, I should have a failing test. in addition to the end-to-end test. The end-to-end test really serves as a background task because the end-to-end test is often a bit broader than a simple baby step. So there already, I want to have this notion of game service, and if the test fails as it should. namely that for now, init returns an empty conduit. Then, it will never observe a game state. Because for now, my conduit remains empty, so the observable remains empty. So Don is never called. So the test is never finished. And instead of waiting eternally, we're going to wait for a timer.
[00:19:27] uh that I still put the signature and that also helps to reinforce a bit the intention of the test. There we can clearly see what needs to be done. So I do I really do my minimum thing for it to pass. Well, that's debatable. It's debatable, there are really many ways to do it, again, this is our own mechanism, it's not necessarily the one to adopt universally. And so at this stage, I should have a failing test, in addition to the end-to-end test, a test point really to serve as a background task. because the test is often a bit broader than a simple baby step. So there already, I want to have this notion of game service and I if the test fails properly. namely that for now, init returns an empty pipe.
[00:20:12] so don't will never observe a game state because for now my pipe remains empty so to speak, my observer remains empty. So don't is never called. So the test is never finished and so rather than waiting indefinitely, we're going to wait for a timeout. So now the test is failing and Agathe's mission this time, if she accepts it. at the same time if she doesn't accept it, I don't know how we'd do it, it's going to be to do the minimum job. to simply already emit uh hop, I'm going to put some I'm going to implement the init method actually.
[00:20:43] I'm going to stand a bit behind.
[00:20:45] Exactly, go ahead.
[00:20:47] I'm going to pass.
[00:20:48] So there, the goal is what? It's simply at this stage, we're just focused on the fact that we want to emit an initial game set. We're not going to do anything else, we're not going to start doing other things. And once and actually the idea the idea of this game service, just to come back a bit to why we went head first into game services, this game service will act as a bridge between what happens in our HTML page, namely I have a form, remember, I try to click on the attempts and submit attempts. And so that must be transmitted to our game service which will then decide the next game state based on the attempt that was made. So typically if Panda I start by submitting the letter P, well then you'll have a new game set, you'll say yes, well done, you found the letter P, so we'll display the letter P and the other letters are still to be revealed and so on. You see that there are states, states that are calculated based on the previous state and the current attempt, and this calculation is encapsulated by the friend game service.
[00:21:47] Okay. I'm sorry for my typing time. It's quite hard to switch to a keyboard.
[00:21:54] And so what's interesting here is that what Agathe did, actually, she used a behavior subject which actually allows, remember it's an observable conduit. So if you subscribe, if you subscribe, sorry for the anglicism, if you subscribe to an observable but the value has already been set, you won't see them, it will be too late. So precisely, the behavior subject allows having a kind of buffer or cache of the last value, and therefore new subscribers who arrive afterwards will see this value. So that's quite useful, it allows us to somewhat distance ourselves from this timing notion which is a bit annoying. So the test passes here. So that's good news but as it is, uh it's not very, very satisfying. We still need to correlate between the keystrokes, so the attempts, the letters we type to guess the word, and the calculation of the game state. because today, at this stage, what does the implementation do? It simply calculates a game state independently of what we type. So the next test, what's it going to try to do? Well, it's going to try to simulate not in HTML terms because we're not at the level of an Angular component, a component that really abstracts the HTML page, so the DOM. to call services, we are at the service level, so we are going to simulate the fact that we try to send a particular character and that this consequently influences the calculated game state. So what's the idea here? Well, first we're going to try to simulate sending a character, and that's where the second parameter of init that I introduced a bit early in the first test makes perfect sense. For now, if you look at line 30, we have an off string that does strictly nothing. And we're actually going to replace that with
[00:23:31] by a conduit.
[00:23:32] a conduit in which we will publish a value. So for that, observable is a part in which we can only read, we will have to create a subject just before the init service and this subject, it's a conduit in which we can also write. And in the test, that's exactly what we want, because we want to produce characters, we want to produce characters that will say the letter we are trying to guess for the word, for the mystery word. So we have a subject and what are we going to do with this subject? So, instead of having an off string on line 31, we're going to publish the readable part of this subject. So be careful, it's shift.
[00:24:07] on a coir.
[00:24:08] Those are the joys of the keyboard when you're not used to it. So we could imagine a character type in TypeScript, or well, that would take more time than what's really interesting, so we didn't do it, we're doing strings which is a bit limited. And so this subject has several uses. First, we'll want line 31, so we have an off string, we'll replace it with a keystroke as observable. What does that mean? It means we're going to allow, we're going to expose the readable part to the service. So he can subscribe to it but he won't be able to write in it. We really only want one to write. And then what do we want, what do we want? Well, we want to write a character in it. So that's what we're going to do, in this subject we're going to write a letter and and see that the game state that is subsequently set uh depends on the letter we just wrote. So typically, this is something we'll do at the end of the test, we'll do it right after subscribe. You see the advantage of TDD here is that Agathe is under a lot of stress.
[00:25:01] That's right.
[00:25:02] She's losing her composure live. But I'm less stressed so I can also help her, you see. It's still the advantage of it even in situations like that. And so the idea here is that we have this subject called keystroke. We'll be able to do a next. Next basically is to submit a value into the conduit and everyone who subscribes will be able to observe it. And so now we can take any letter.
[00:25:25] For example.
[00:25:27] For example, the letter C. So, the last part remaining is to adapt this subscribe. because remember in the first test we saw that I can't speak anymore, we systematically liked an initial state. So we know that, but this time what we expect is that now that I've sent a letter, I expect a game state to have been calculated, that my game state changes, so I want to see a new value. So for that, the "if" won't necessarily happen immediately here, it will rather happen in the subscribe, but we'll have to count. One way to do it is to say that we have the initial state, that's from the previous test. We send a second state, so for that we need to count.
[00:26:02] We will have a second gap state observed by the subscribe. And if we reach a second state, then we will indeed observe that its characteristics take into account the attempt we just made.
[00:26:17] That's going to be good.
[00:26:19] And so we're going to arrive at the second game state, so we start at zero, so we're going to arrive at one. If we reach one, then we can do our expect and our done to signal that the test is finished.
[00:26:32] a curly brace is missing.
[00:26:34] I had put it for.
[00:26:35] Yeah, but you you must have deleted it weirdly. There you go, and so what will be missing next is to increment this counter otherwise. Command Alt L if you want.
[00:26:47] I'll do it if you want.
[00:26:47] No no, it's fine, don't worry.
[00:26:48] I will. Command Alt L and that should reformat everything.
[00:26:52] Option L.
[00:26:54] Yeah. OK. And so we need to increment the counter otherwise we'll never get to
[00:26:59] Yes. So.
[00:27:04] And so if we look at what's happening on the test side, So our intention there, I think it's declared clearly enough, we're going to try a letter and we expect that, well almost, not quite, the test isn't quite, I was speaking a bit fast. It's that the left times won't be max times, we won't have the max number, we'll have max times -1. because we've just used up a cartridge, so to speak, we've just used up an attempt because we just messed up basically. And fail the tems this time won't be point size, it will just be fail the tems, it's going to be the set of failed attempts which should notably contain the letter C we just tried.
[00:27:37] And there, finally, we have a correlation between the game states that are calculated and what we are sending via this observable. And once we have this bridge, the doesn't exist yet, but we can settle for the character type for now. And so at this stage, that's it. Now our intention is clearly declared. So we're going to see if the test, if the test passes, that would be very surprising. But the test a priori won't pass because we only emit a single initial state and so we'll never enter the if and we'll never call done and we'll have new timeouts because the test will never complete. So that's okay. And so now, I'll stop talking and I'll take over to try to correct things.
[00:28:15] We're going to try to correlate between the letters that will be shown and what we have as output. what's going to happen. Do you want to put it in full screen?
[00:28:23] Yeah, I'm going to put it in full screen actually, just so it's clearer for everyone. Okay.
[00:28:29] Uh, we're going to subscribe to input events, so the keystrokes.
[00:28:35] And the goal of the game will be to publish a new game state based on the events that occur. So,
[00:28:50] it's good.
[00:28:52] OK. So in fact, this behavior will be encapsulated within the game state class, with, well, it will be the compute next state method that will produce a new game state after each attempt by the player. So we're going to encapsulate it in this class.
[00:29:16] and it will take the current attempt as a parameter and return a new game state.
[00:29:24] So knowing that the objective here is to have one less attempt since we tried a letter that didn't work, that wasn't part of the word to guess. and uh to add this letter to our unsuccessful attempts. There.
[00:29:43] That's done. And there, you see that we're really on a literal implementation, it's not at all complete but in the current state of our test, it's enough. Cool.
[00:29:54] So now the question remains, uh well, we have a game service that bridges correctly, which uh which takes which observes the and asks to calculate the game state accordingly. Is that enough for our test then? Have we finished? So, we can rerun the test to be sure, remember, we had a test that was failing. on I select a language and I want to see that the failed attempts are properly displayed.
[00:30:21] And then it takes a little time but I don't know, I feel, I sense that the test still won't pass. And incredibly, it still doesn't pass. Why? Because actually Game State.
[00:30:31] exactly.
[00:30:33] what we showed at the beginning.
[00:30:35] that can't work for.
[00:30:37] So we're going a bit fast here, we're going to uh we could test drive it too, we could drive it with unit tests. We're going to go a bit fast here, we're not going to do it with unit tests. Uh and so what we're going to do is that instead of hardcoding this famous game state observable, which basically will only give me one value throughout its life, we're going to more intelligently go through the game service we just completed. And tell him, well, here's the initial word.
[00:31:02] which should actually be called - I'm going back to full screen, sorry - which will be called something, for example, "Word". and uh the famous inputs, so the observable, where will it come from? It will come from input, and actually if we look more precisely at what's happening, if we look at where it comes from, you'll see that in fact every time we submit the form.
[00:31:24] with a character, we will publish in this subject.
[00:31:30] So that's how, in fact, every time we initialize the component, it will initialize a conduit, a channel - I don't know how else to vulgarize it, let's say a channel - in which we will write each time we submit the form. So every time we type a letter, we press enter on the page, it will send via this next method a new a new character and so this time that's it, the game service will take over and calculate the game states based on what has been passed. So that's cool, the question is, is that enough for the tests to pass now, the unbearable suspense.
[00:32:09] and now, attention, everyone is holding their breath, we can't take it anymore, there's so much suspense. And it passes, wonderful, great. On the other hand, well okay, we're taking a short break, we're at the office, it's coffee break, and we say to ourselves, "Yeah, but wait, I think we haven't quite finished yet when we discuss."
[00:32:23] And if we look, well, notably the game service, it actually looks pretty good. It uh it has an initial game state and then we're going to ask that game state each time, we're going to ask it each time eventually, we'll even change it a little along the way. So that's not too bad. Since we're doing something mutable, it will actually work. It turns out that if we had done it immutably, there would be a bug, but again, if you want to see a slightly neater implementation, we'll send you the repository if you're interested.
[00:32:54] So since it's mutable, it will work to take it like that, it's a bit ugly but in reality, in reality, we should do it a bit differently. But whatever. In any case, it works roughly, so it will observe the characters we send it and calculate a next state afterwards.
[00:33:08] But then, what is it that
[00:33:09] that it works in case of failure.
[00:33:11] Exactly. Remember the implementation of compute next state, it systematically says that we lose an attempt and that everything we try is wrong. Which is not exactly the implementation we'd want to go towards, so, well, we're going to start, we don't necessarily have, we could add an end-to-end test, we don't really have the time, we won't do it. Uh but we're going to complete the tests. We're going to complete the game state tests, and the advantage here is that we're completely isolated from observable, not observable, it's much simpler, we just have game state and attributes to pass to it, and we're on slightly less technical tests than before.
[00:33:41] the technical part is finished.
[00:33:43] You can go.
[00:33:45] Me too. And so this time, we're going to start, if I'm not mistaken, because I couldn't even remember what we want to do.
[00:33:51] by a successful attempt.
[00:33:52] With a successful attempt indeed, because you see that there we systematically fail even though if it succeeds there's no reason for it to work. So.
[00:34:04] Hop, something like that. We want to take into account the, well sorry for the English, it might not be completely correct but English-speaking bilinguals in the room, we apologize. So what do we have? So we want a game state. Actually, I'll stop talking now because I need to code, I need to concentrate, sorry.
[00:34:23] Uh so we have a game state and this time we're going to see we're going to try to make assertions about what could happen if the player manages to find a letter. So there, remember the word to find was Panda.
[00:34:36] So we're going to search with the letter N, we're going to restart the same method which should produce a new game state at each user attempt. And we're going to check that the number of failed attempts will remain at zero. So it will be zero. And we're also going to check that the number of remaining attempts won't decrease, in fact, because when you find a match, we keep our number of attempts. Otherwise it will be a bit complicated.
[00:35:11] Uh, we're also going to check that uh we will indeed display the failed attempts because the goal of the game is to also show the player that the letters that don't match the word. Finally, we're going to show him the letters that don't correspond to the word to find, because otherwise it will be a bit complicated for him to remember all the attempts he made before.
[00:35:36] And there you hear the question, why is it a five-letter word? What makes N in the word or not? That's an excellent question because today, uh game state absolutely does not take into account the word we are trying to guess. That's something we're going to have to correct immediately. Otherwise we're going to have a little problem. Otherwise the test in its current state makes no sense. The intention is not clear at all.
[00:35:57] So we're going to add the word to guess to the game state. So here, Panda.
[00:36:05] he's not happy because there's no constructor that doesn't take a default constructor.
[00:36:10] We're going to complete the Game state class.
[00:36:13] There, it's much prettier. or not.
[00:36:21] And a little magic word, if you put 'private' in front, it automatically becomes a field. So what I just did is cute, but it's completely destructive, we're not refactoring at all, I potentially broke everything here. So we're going to rerun the tests to launch that, but unfortunately, we didn't have a choice then. Otherwise, we couldn't have gone much further, so the tests will encounter compilation problems.
[00:36:40] An argument for word was provided. You see that there are several places where I create a new game state without passing it any words. So naturally, he's not happy. The good news is that it will be corrected quite quickly because I already have the word at hand. we'll take that as we go. We'll already have that. We're going to let ourselves be guided by the, in this case, having a compilation is quite practical, it allows testing all the problems at once.
[00:37:07] It's hot.
[00:37:09] Yeah, so we're going to make it short for you. What's the problem? It's that init is supposed to emit an initial game set. but in the game in the component's unit test, we isolate ourselves from the outside, including the service. And the service too, you see that for now, it's not faithful to the implementation we made. In fact, what we should do is not emit, if I'm not mistaken, a new initial game state. Otherwise it's no longer consistent with what we've done, that's why it's a failure. In reality, we should have spent a lot more time understanding, but we won't do it now because we know where the problem is. In real life, it takes time. But it's okay, you have to accept taking your time.
[00:37:47] the first time, the first time we coded the application, we took a lot of time.
[00:37:51] Exactly, on problems like that. And so this time we have a failing test and if all goes well, it's the right one and then miracle of miracles, it is indeed the right one. So I'm going to scroll up a bit because it feels a bit like magic, but.
[00:38:05] Game state for successful attempt. And so now the test is failing and so, I stop coding, I hand it over. to.
[00:38:12] the camera here.
[00:38:14] Agathe, I need to stop, I need to stop looking at my laptop, I'm using the wrong camera, it's my fault.
[00:38:18] Okay, so the goal of the game is for me to fix the test. Uh
[00:38:23] I skipped a bit because I had everything closed, I think.
[00:38:24] Yeah, that's it.
[00:38:25] You need to open it, we need to go back to full screen too.
[00:38:28] Yeah, I'll do that. OK. Uh so I'm going to go directly into Game state and actually, I'm going to re-edit the compute next state method which only worked for failed attempts.
[00:38:41] Hop. So what we're going to do now is to check that this explains.
[00:38:46] Yes, no, but actually, what I'm telling myself is that I'm going to let you concentrate and then I'll explain what you're trying to do. And so the idea here is that the compute next state, for now, was quite blind, it told us, "Anyway, you must have failed." That's what it says actually. but in fact it's not true, we still need to check that the word, so finally we have access to the word since we introduced a constructor. The word to guess, we'll just look at its letters. We're going to look at each of these letters and say, "Well, is the attempt I'm sending you right now, is it part of these letters or not?"
[00:39:14] And so splitting on an empty string allows you to retrieve, it's a little shortcut that allows you to retrieve all the letters into an array. And in fact, we could ask for index of, what is the index of the first element that matches in the array of. And so, if we effectively find an index, it will start at zero or more, we'll be greater than or equal to zero, so that means the character we're looking for. is indeed in the word. If we don't enter the, it means we're in the case where it's an unsuccessful attempt, and that's what we coded below.
[00:39:44] Exactly.
[00:39:47] So we can just leave it as is actually. If you found it, if you found it, that's cool. But the question is, is that enough? Is it enough for the test? Well, we'll see. Hop, the tests are running continuously.
[00:40:01] I'm talking about it.
[00:40:02] Yes, it's true, I'm off-camera. But then, that's not enough. And why? Because actually, it turns out that if we look at the test, if we go back to the test in question.
[00:40:16] You see that we call displayable charge, it's a method on a next state that will display the word in its current state, meaning whether the letters to guess and the letters have already been revealed. And if we look at the implementation of displayable charge now. So we can just leave things as they are. If you found, if you found, that's cool. But the question is, is that enough? Is that enough for the test? Well, let's see. Oops, if we... The tests run continuously.
[00:40:00] You were talking about earlier.
[00:40:01] Yes, it's true, I'm off-screen, but, uh, so it's not enough. And for why? Because in fact, uh, it turns out that, uh, if we look at the test, if we go back to the test in question,
[00:40:15] You see that we call displayable chars, which is a method on next state that will display, uh, the word in its current state, namely, uh, are the, well, the letters to guess and the letters that have already been revealed. And if we look at the implementation of displayable chars now,
[00:40:32] There you see that we systematically make a table of five elements with the question mark. So it's not, it's not at all the right implementation.
[00:40:42] And, uh, and so that's where I realize I skipped a step. by adding that directly to the test, but that's okay. So we're going to do it together. So what's the idea? Well, in fact, it's not only...
[00:40:50] we're going to start with displayable chars, it's to have the status of the letters in fact, if each of the letters of the word has been revealed or not. Well, we're going to start from the end, so we're going to start with displayable chars.
[00:41:01] And what we're going to do, so we're going to take uh the this.word.value, so that's our word.
[00:41:08] So that's the, the, the character string associated with the word to guess. We're going to once again do a split to retrieve the characters one by one.
[00:41:18] So there it is. So here we're going to retrieve a table of characters and then we're going to transform character by character via the map function. You have to do point map.
[00:41:25] Yeah. It works. I'm having trouble with the point.
[00:41:29] Yes, the joy of the QWERTY keyboard. And so what are we going to do? We're going to take a function that takes character by character. So we're going to see the letter.
[00:41:40] There you go, the letter. And so there we're going to write code that doesn't exist yet, so we have to do the equal arrow for the callback. And we're going to say, uh this.word, oh yes you can do that too, it works very well. It's more readable, it's even better. This.word state. So this is something that doesn't exist yet, but we're going to explain it right away. Word state is something that will associate a letter if it has been found or not. And so word state with if we open the brackets of letter, so basically it will ask us, it will give us the status of the letter in question. And so if it's true, so if we do if on it, if we put an if around that.
[00:42:17] Uh so if it's true, that means the letter has been found, in that case we display it as is.
[00:42:25] That's the first if. So we can, we can, we can, we can, we can compare it to true.
[00:42:28] Ah, no, that's it.
[00:42:30] We can put it directly like that. So in that case, we can do return letter, because if the letter has already been found, we can display it, we have the right. If on the other hand, uh, so in the opposite case, so outside the if, uh, if it's false, that means the letter has not been found, that's where we display the famous placeholder, the famous question mark.
[00:42:53] And so there you have to display, you have to do return placeholder. And so, and that, and so, in fact, line 15, you have to put a semicolon, you have to put a semicolon before it. And remove line 21. Line 21, so it has no more utility. We're going to do a return of that.
[00:43:06] Okay, that's it.
[00:43:07] So a return of this. There you go, return of the transformed table. Line 21 disappears because it was just a default implementation so that there was something. And finally, well, there's the famous work state to define and initialize correctly and to update. So that's what we're going to do, we can do alt enter to add this new field.
[00:43:27] There you go. So in fact it's going to be, we can, we can emit, omit the type for now, it's going to be a simple JavaScript object. And what we can do with the in the constructor is to initialize it. In fact, at the beginning, by definition, when we start the game, we haven't found any letters. So we can iterate over each of the letters. So the same way, this.word.value.split.
[00:43:48] We're going to copy.
[00:43:49] There you go.
[00:43:53] And so for each of these letters, uh, we're going to do a simple for loop over it. We're going to do for letter of that thing.
[00:44:03] And so that's going to give us, in fact, uh, letter by letter, and we're going to initialize then the status of each one.
[00:44:08] For letter, for let letter of this.
[00:44:13] Or const, even that will work. For const, yes, const. The parentheses around the const.
[00:44:19] There you go. You need parentheses around that.
[00:44:22] There you go, and then the parentheses go up. And you can put a parenthesis at the end too.
[00:44:27] You see, the joy of pairing is that you have immediate code review. That's what we were telling you.
[00:44:32] And so, in the for loop, we're going to modify an object that doesn't exist yet, namely the word state.
[00:44:39] Uh and so word state.
[00:44:42] Brackets of the letter.
[00:44:44] Okay, that's it.
[00:44:46] So it's going to be, we can do that, we can do that effectively. And so, with brackets, letter, and it will be false at the beginning. necessarily because each letter, no letter has been guessed at this stage. And so, and so we're going to initialize everything to false. However, so that it doesn't crash, this for loop, we're going to have to do something just before the for loop, we're going to have to initialize this.word state to an empty object, otherwise it will necessarily crash. So that's the first part of the job, we have defined and initialized the field.
[00:45:15] Just, just a curly brace, that's enough. We can make a new object of sorts, but it's a bit overkill. And so the last part, which is, well, simply to update this word state when in case of a successful attempt, because by default everything is false. And so if we go down a bit further, if we go back to the compute next state, what we'll have to do is if we find the word, so in the if, just before the return this, we'll have to add, uh, so we'll pass it to true, in fact, this.word state of the list. So the letter, the status of the letter. that we just tried. And so we're going to pass it.
[00:45:54] The joy of the QWERTY keyboard as always.
[00:45:55] Yes, oh, it's hard.
[00:45:58] And it will go to true. And there if all goes well, if we come back on the tests.
[00:46:05] Let's see.
[00:46:09] If we go back to the tests, there we should have the test that passes. Okay, so the test passes, that's it, because we actually have a correct implementation of the display of the word and also of the state, so our test is focused on the case where it works well.
[00:46:23] So, well, now Agatha will introduce a new test, namely, for example, the fact of not taking into account an unsuccessful attempt if we have already tried before. If for example you have the word panda, you try the letter X, if you use it twice, in fact we will ignore the second time, which it seems to me is in the rules of the game. So for this time,
[00:46:43] it's going to look a lot like the previous test, it's just that, uh, we're going to do two compute next states, we're going to do and twice for the same letter that doesn't exist in the word. For now, we're going pretty fast, uh, we're not going to go into the details, so to speak.
[00:46:57] Exactly.
[00:46:59] We ignore duplicate failed attempts for example. Because we don't, it seems to me in the rules of the game.
[00:47:09] So now I see the question in passing, what rules to give oneself for tests, if it can correspond to a software architecture intention. And not necessarily an expected behavior by the user. So, it's a question that would deserve a much longer discussion, in my opinion. In my opinion, I'll let Agatha continue on her test so that she doesn't get distracted.
[00:47:29] But for me, the software architecture, it translates into the implementation, in fact. Not by, uh, well, it can be discussed, it can be discussed, but for me, really, we try as much as possible when I'm on a user story.
[00:47:42] A user story, it's an anecdotal fictitious anecdote of the user who tells one of the facets of your product. That's what a user story means at its base. For me, it's centered on the user above all, and so it's tests that are rather oriented towards user actions. And user is not necessarily a human, it can be, it can be an API client. It can be another system when I say user, it's in the broad sense. For the coup for the architecture, yes, I don't necessarily have a great answer on the subject of software architecture. For me, it's more about the implementation and potentially, these are automated controls that you can add afterwards. There are tools like JQ assistant or this kind of thing that allow to reinforce, for example, things that a DAO will not inject into a controller or a service or this kind of architectural rules, but that for me is not, it doesn't fit into the. in the current test flow, these are tools that can be added systematically afterwards. So I don't know if that answers the question or if it's completely off topic. But there you go, in any case, don't hesitate to ask the question, to rephrase it if there are aspects I haven't understood. And so what do we want to do? Well, we do two compute next states in a row. So ideally we chain it, even if here we do everything by mutability to go a little faster today, but. But we can also chain it because in real life the real implementation should be equally immutable and.
[00:49:02] Just the little dot that's missing.
[00:49:04] No, wait. Let me do it, I'll put it.
[00:49:08] Does ping-pong go as fast in real life? Uh, no.
[00:49:11] Not at all.
[00:49:12] That's why we, as I said at the beginning, when we started live coding, it's an idealized version because it can take time. However, however, the ping-pong mechanic is simple. Even if you're on a complicated problem to solve, the mechanic is simple. It's still good to stick to it, it's still good to have the patience and the discipline to do it.
[00:49:28] When we start TDD or pair programming, it's like that in fact, we attack something new, so, well, as you can see, mine is still in acquisition. As you can see, mine is still in acquisition. And uh, you have to accept that acquiring a new skill is long.
[00:49:41] Yes. And so, I'm taking the floor back violently because I know that Agatha has to concentrate on her test.
[00:49:47] Her test failed.
[00:49:49] And so you'll see that in fact, in the present case, we'll only have, uh,
[00:49:54] It's horrible.
[00:49:55] So it's not going to be, yes, you have to remove the point size.
[00:49:58] Yes, yes, yes.
[00:49:59] So you'll only have one N because it's a set, so that's, that's provided by default, that's cool. And the left attempts, we'll have lost only one because in fact the N we already tried before. So yes, no, to go back to the question, does ping-pong go as fast? No, it's idealized, we already know more or less what we're doing, which is rare in real life, in general we're more in the unknown, which justifies even more in my opinion the test-driven development which really forces us to think step by step.
[00:50:20] And to give you a little anecdote, one of the first projects I did at Pivotal Labs, uh, the two Pivotal Labs developers, so me and a colleague, we had an Angular stack, we didn't know Angular, we knew Angular JS but we didn't know Angular. I think we, we took two days the first time to pass the test or something like that. And it seems huge, and in environments a little bit unhealthy, unfortunately, spending two days trying to fix a test is not something that is well-regarded. And yet, for two weeks, we struggled, struggled, struggled, but we stuck to this discipline of ping-pong, TDD, and pairing. And we still shipped a first version into production in 3 months. And we knew nothing about Angular. So really, uh, you shouldn't be afraid.
[00:51:00] Okay, so it's a failed attempt. So I made a little mistake, it's a, it's a double check. So necessarily.
[00:51:06] And I'm putting the wrong letter. The B is still not good. The B, for example, is not good, it's the letter X.
[00:51:11] There you go.
[00:51:13] And so, we'll have no word, no display of character because we haven't guessed the N.
[00:51:19] The N, not yet. Oh, yes, exactly. So that disappears and, uh, it disappears or it corrects, but it's the same.
[00:51:24] It corrects. It's okay, it's okay, leave it like that.
[00:51:27] Very good. That's enough.
[00:51:29] And in fact we're going to decrease the number of remaining attempts by one and not by two.
[00:51:35] There you go.
[00:51:35] Because we're not going to count the same letter twice, it would be a bit unfair. Okay. So I finished and if we check the test.
[00:51:42] We're going to see if the test passes or not, if the test fails, obviously. Hup, great, it failed, so it's my turn, so I'm going to talk a little less. About the keyboard question, yes, at Pivotal Labs, we each had our own keyboard, some had QWERTY, some had AZERTY.
[00:51:54] Most of the time with Florent, we do pair programming remotely, so everyone has their own equipment, it's much simpler. Here it's exceptional because the dictation is a bit more complicated to manage.
[00:52:10] Uh, how do you manage shortcut habits, for example, in varied teams?
[00:52:16] Uh, well, it depends on who.
[00:52:20] who codes, in fact. The one who codes can keep the format of his shortcut habits. I learned a lot of shortcuts because Florent, every time I do something, he gives me a shortcut directly. But, uh, there you go, the goal is always to increase everyone's skills, to help each other improve. So, well, why not teach the other new shortcuts or things.
[00:52:50] that he is not used to using, like the date or things like that. That will save time.
[00:52:57] There you go, it's very particular. I'm not at all used to the QWERTY keyboard, I'm still struggling a lot.
[00:53:03] And with the stress of the presentation, I have a small memory lapse on the next test, but in real life it's a discussion. What is the next test, what is the next step? It's a discussion in your pairing. So it will just assemble, except that we had decided in advance but I forgot.
[00:53:14] It's especially that you reversed everything you had to do.
[00:53:16] That's it. That's it, I'm a bit off schedule.
[00:53:18] So in fact, we said that we were going to ignore the duplicates of the failed attempts. So now we're going to do the same for the successful attempts.
[00:53:26] Yes, so that changes.
[00:53:28] Once we've guessed a letter, even if the player indicates the same letter again, well, it's not going to do anything in particular, in fact.
[00:53:36] So let's see if it's the case, if there are things to do or not. So for now, you see we don't do much refactoring. Not that it's, not that the code is perfect as it is, it's just that we're running a bit behind schedule.
[00:53:50] So what do I do this time? Uh, I have two P's.
[00:53:55] For example.
[00:53:59] I have an empty set of failures.
[00:54:03] Uh, and I have left attempts, well, I still have to be at max because I've already found. And then I'm still going to reintroduce my friend displayable chars because I like him.
[00:54:14] Uh. Hop. And this time, I'll have found only one P, so that should, that should change nothing. Suspense, is the test going to fail, and am I going to be able to pass the hand to Agatha?
[00:54:26] So the test passes directly, indeed, we have nothing else to do on the implementation. So the purists might tell you to remove the test because it brought you nothing. We can decide to keep it, because finally, it's also non-regression, it can be useful. So there, frankly, it's up to you to see too, we don't have, we don't have absolute truth. I'm going to keep it for now and we're going to move on. If we don't have much time left. So, we're going to overrun by 5-10 minutes, I hope the audio. won't be too much. with us. So, we're at, ignore the player's attempts.
[00:54:58] In fact, it's the last step. When the game is over, in fact, we don't take into account anymore.
[00:55:03] the letters that will, that will reach him. Yes.
[00:55:11] Uh, when game is one, I'm going to do that instead, I don't know, I feel good about it.
[00:55:15] Or no, game is lost, because that will be easier. Sorry. I'll do it like that for now.
[00:55:22] Yes, because in fact, once the game is over, you can guess as much as you want, it won't, it won't change the stats in fact. So here again, we're going to do some ugly copy-pasting, we're not going to refactor, put things in a for each, that's something we're not going to do. So typically, I lost, there I'm going to cheat a bit. I'm going to change, there, we're going to do two.
[00:55:45] So if I try B, if I try O, well normally the game is over. And on the other hand, if I try a, I don't know, a small. There you go, bov. I don't know anything about it.
[00:55:56] For me, it's always in white.
[00:55:58] Yeah. So that's good, it's failed attempts.
[00:56:00] Failed attempts, it's the number of attempts.
[00:56:02] Yeah, I did it on purpose, I cheated.
[00:56:04] So in the failed attempts, I should have only B and V, the O shouldn't be taken into account because in fact the game was finished, so since I forced to two max attempts here.
[00:56:13] Yes, so B and O.
[00:56:15] Oh yes, B and O, sorry. But you see, instant code review, that's a bug that I would have spent minutes or even hours looking for afterwards. And so on the other hand, I wouldn't have found anything because I'm not a very good player of Penda. And so now, suspense, does the test pass, does the test not pass?
[00:56:33] The test, a priori, does not pass because you see that it took the V into account when it shouldn't have. So I'm passing the hand to Agatha. And, uh, I talked too much while I had the keyboard.
[00:56:42] Uh, it's not serious. But what I'm going to do in fact is I'm going to complete the isLost method, uh, this method.
[00:56:50] Yes. I can talk in your place if you want, even if you already talked a lot, but.
[00:56:54] Oh, no.
[00:56:56] That way it allows you to focus.
[00:56:58] It will activate in fact when we have no more attempts left.
[00:57:03] Yes. So the idea is effectively, well, if the game is over, so we're going to start with the lost aspect.
[00:57:09] It will be a little, a little simpler to implement. Well, it's lost when, as Agatha said, when there are no more attempts, no shift.
[00:57:17] Okay, okay. Okay, yes.
[00:57:19] If I have zero attempts, well, it's finished. So it's as simple as that.
[00:57:25] So that's the definition of the. You'll need a small return and then remove the return below. So that's enough to say that the game is lost. Now, we need to compute the next state to take that into account. If the game is lost, well, I don't calculate anything at all, I stop there. So I do, if the game is lost, I do a return this, simply, I don't calculate anything else.
[00:57:41] So that means the first thing to check is if the game has been finished or not.
[00:57:47] Or rather if the game was lost, sorry.
[00:57:49] Exactly. This is lost, there you go. I return this.
[00:58:01] Okay, that's fine.
[00:58:02] So, does the test pass, unbearable suspense.
[00:58:06] And in the same way, if it passes, we'll see, well the idea will be to do the same way, even if the game is won, in fact. If the game is won, it will do nothing, it will change nothing.
[00:58:16] And so there you have to pass, it will look like the previous test. And since we don't have much time, we're going to copy-paste again like crazy people. Do not reproduce this at home without the assistance of a professional, of course.
[00:58:26] Uh. We're going to overrun, we're going to overrun by 5 minutes, I think, I hope it's tolerable. Otherwise, well, otherwise we'll be.
[00:58:33] I only have one attempt left.
[00:58:35] And so this time, it's a bit the reverse, it's when the game is won.
[00:58:39] Okay. Uh, we're going to put Bob, it's too fast, it's the name of the panda.
[00:58:46] Uh, exactly.
[00:58:47] Exactly.
[00:58:49] Hop. So I remove that.
[00:58:51] The fact of doing BOB this time, it will allow to win the game because there are only two distinct letters in BOB.
[00:58:55] That's right.
[00:58:57] And we're going to send V, which is actually an unsuccessful value, but since we've already won the game, in fact, there will be, in the failed attempts, it will be empty.
[00:59:05] Uh, left attempts, well, I'll have some left, in fact, no matter what, in this test. But we can just say that it's max attempts, because we haven't wasted any in the absolute. Ah yes, sorry. this.next.max attempts. Ah yes, sorry.
[00:59:15] Ah, oui, pardon.
[00:59:16] It turns out that it will work because we actually have more of a constant than anything else, so it's a refactoring that we could do and not attach it to an attribute of the next state object.
[00:59:25] Max attempts. Okay, that's it.
[00:59:28] And so the word we display will indeed be BOB because we found all the letters, and the fact of having tried to guess V afterwards will have no impact.
[00:59:35] In an ideal world, we'll see if the test passes or not. So the rest goes away.
[00:59:40] Hop. And so if we look at the tests, does it pass, does it not pass?
[00:59:47] Normally, that should be more or less the last test we're going to do.
[00:59:50] Oh my god, oh my god, it doesn't pass, quickly, quickly, let's correct it. And so I stop talking, promise.
[00:59:58] The goal there is to, well, to add another condition if the game is won. We stop there. And to complete the isOne method.
[01:00:11] There you go. Okay. In fact, we're going to check that in the word state, all the letters have been set to true. I lost my mouse. Sorry.
[01:00:25] Because remember, in the word state, the letter goes, uh, yes, each letter found goes to true. If the whole thing is true, that means the word has been found and the game can stop because it has been won by a player. And so, what we're going to do here, we're going to do something like.
[01:00:50] In fact, I'm doing a reduction, I'm doing an 'and' on everything, and if I have a false in the middle, at the beginning or at the end, if I do false and something, it's necessarily false. So the isOne will only be true if everything is true. So it's a clever way to do a forEach and check that everything is true. But normally, if all goes well, that should be enough.
[01:01:11] Woohoo, it passes, great. So in theory, and there it's going to be a bit of a suspense, we're going to relaunch the app and we're going to see if the game behaves a little better than before, because before we could type anything, nothing happened at all. For that, I'm always going to do my fake backend, I could also launch the real one, whatever, it's just a little faster like that. We're going to relaunch and normally at this stage, we could have an end-to-end test that really validates the functional flow of the game.
[01:01:33] That's what we would do in real life if we had a little more time, we would have another end-to-end test that would tell us, well, when I guess a word, the word is displayed and I can't play anymore afterwards, or when I guess a bad word, I lose the game, etcetera. So that could be two additional end-to-end tests that would allow to verify that indeed, that indeed we have won or lost. And so now we're going to look, unbearable suspense, so the word to guess, it's my fake server with the hardcoded word, it's panda. So if I do R, well, it lists the letter R correctly, so that's pretty cool. I have one less attempt, if I do G, it's not in it either.
[01:02:08] If I do, uh, A, oh, it's in it.
[01:02:11] If I do I, it's not in it, if I do K, it's not in it, well, you understood the principle. There's a magnificent animation, I think we're going to be billionaires before this game. And so, there you go, and if I try other things, well, R, you see I already tried it. I can try it again, it doesn't change the state, so that's good what the tests have allowed to prove. W, oh darn, and then I'm going to, and then I'm probably going to lose. Okay, so here we go, the sad panda, and that's it, it's a test. And just verify that we can win anyway.
[01:02:36] Uh, so I have P, I have A, I have N, I have D. Yay, we won. Well, there you go. So, that's it, we're done, approximately. Well, you'll see that the implementation, so if you go, it's true that. We can probably send it on the chat too, but if you go to this repository, you'll have an implementation a little less messy, so to speak. To keep you busy during the long confinement evenings. There you go. So you just have to, either you make a fake server and you don't need an account, or you go to Lexicala which is an API service to have a bit more random words, so potentially a bit more complicated to guess. And all the instructions are there, you can also see the implementation if the code interests you. With a little more tests, a little more immutability, even if it's not a lot of refactoring.
[01:03:14] Exactly.
[01:03:16] Exactly, we really did the raw version from scratch without refactoring, which is not necessarily recommended in a.
[01:03:23] There, if we had had time, we would have done a little more refactoring. I still did a refactoring that I didn't necessarily comment on earlier. The left attempts, at the beginning it was a field, and now in fact I made it a property that is simply based on the number of max attempts minus the number of failures, the number of failures. So that's enough in fact to solve the problem. That's how I made a test pass. And the objective of what we wanted to show this evening is that you shouldn't be afraid to try new ways of working.
[01:03:49] Like TDD or pair programming, or even both. But you have to put yourself in good conditions to learn. That's why I don't recommend, for example, starting TDD on legacy code. I've done it, it's not very, it's quite hard. When you're not accompanied, it's rather complicated. In solo, it's clearly hard. But, uh, there you go, the goal of the game is, uh, it's, well, in any case, we think that it's good to also appropriate the methods we use. And sometimes to change them, to modify them a little bit so that it corresponds to our way of working, to what we like to do and that the pair works well. And that we manage to produce better quality things with, especially in fact, the most important thing for me, TDD and pair programming, it's this kind of adrenaline every time a test passes green that we can also share with someone. And, uh, there you go, I think it's a great way to work because it's extremely motivating and it allows us to produce better code. So don't hesitate if you have, uh, if you have any questions, if you want information on good practices to follow.
[01:04:56] Uh, there you go, we're available there, right now, on Twitter, there you go, there are several ways to contact us, we can give them to you. But, uh, there you go, the goal of the game is to show you that it's within everyone's reach. Even beginners, and that even if you haven't understood anything of what we've done. Uh, technically, uh, the goal of the game is to show you that you're in an exchange and that when I lose my way, Florent is there to help me, and when he forgets, uh, where we are, well, I can remind him that we're at such and such a point.