Bootiful TDD by Josh Long @ Spring I/O 2019

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] all right hi everybody I am going to we have very little time as always we have very little time so we dispense with important business first and then we'll get to the talk I want to get a selfie I need you all to pretend like you're happy and just and just say open-source when I say open-source and we'll take a photo together and it's gonna be awesome okay so okay okay ready steady open sores and Othar this open sores thank you my friends I appreciate that thanks so much for coming how are you oh that was too fine you're in I I kind of understand that I mean have you seen outside it's paradise and here we're you know I love I love software as much as anybody can but beautiful Barcelona is all around us so we are we are a very little time so I want to encourage you to take note of this slide you can see at the top there's a git repository that repository is actually for an eight-hour online training that I do it's eight hours of training and there's lots of stuff there including what we're gonna talk about today but also a lot more so you know you can have all that for free I'm sure you'll it's pretty kind of you know once understand the basics we're gonna talk about testing we're gonna go through a lot of stuff today okay the goal is to go through a lot of stuff but some important parts that we're gonna cover for only a few minutes are actually going to be covered in much more depth by Andy Wilkinson he's got a talk on testing as well in spring and that's a very good talk I would you know that's the talk that if we could merge this together and include his and then and then include mine around that that would be a good story but I recommend you enjoy this and then we're gonna we're gonna go through a lot of stuff and then you go see Andy wilkinson's talk cuz that'll have the details on a particular segment of this discussion okay a little bit about me my name is Josh long I work on the spring team I'm on Twitter how many of you just curious aren't twitter 2019 i always wonder about this twitter twitter 2009 kiin twitter twitter all right the rest of you what are you doing get on it get on Twitter it's a good place to be it's the new IRC it's for the developers that drive the open source that power of your business are and it's fine I'm also available by email how many of you have email email is email a thing do you use email I don't I'm not really a big email person but on the other hand I prefer it to slack right I have a brand new MacBook Pro with 32 gigs of ram 32 gigs of ram that's a that's enough to run almost a slack right it's a big deal so that that's exciting for me I still prefer email to that write a little bit about me I work on the spring team you know that's cool I'm very lucky there I went to China and they made a little toy out of me that's nice I have training videos on Safari these are online you can find all sorts of different content there these are sort of in-depth discussions of of different topics and these are usually you know four or five six seven eight hours of content I've got a book called cloud native Java all about how to build applications that survive and thrive in the cloud I've got a podcast called a beautiful podcast every Friday you can go to Google Play or of iTunes or whatever whatever find podcasts are downloaded you can find that their screencasts every Wednesday so every Wednesday I do a new screencast I look at some part of the spring ecosystem so the thing that we did yesterday was all about using Kafka from spring batch which i think is super interesting and I've got a new book oh you want that photo okay and we've got a new book a new book called reactive spring and this is self-published it's on lean pub you can find it there right now you can already start reading it there's already 200 pages of stuff and I'm gonna add more if you buy it now it's cheap and then you can you know when the final book comes out you get the final edition but you don't pay any extra cost you buy the cheap price now and then it's cheaper than the final Edition cost later so all this stuff all this stuff so why would I spend time with you today if I could spend time talking about anything why would I talk about testing and I think testing is one of those parts of the sort of spectrum the continuum that we have to deal with that we don't really think about until we get to a place where we need testing what I mean by that is we have been talking about micro services and cloud computing and continuous delivery and all these things that motivate or rather they support the goal of building software faster of moving value from concept all the way down to customer faster and that's been something that people in general have been talking about for the last ten years I've certainly been talking about it a lot micro services and continuous delivery and agile and all these things are in in service of deploying software to business you know to deploying business value to production faster when you get down that path one thing that you very quickly discover is that in order to do this in order to really take advantage of this fast movement you need to have safety as well right anybody anybody can drive a real fast car straight into a wall that's not very useful speed by itself isn't very useful what you need is something to sort of calibrate that to to measure to meter it and that is a test harness right in our case we want validation that every time we introduce a change and as quickly as we introduce a change that we know that that change hasn't broken anything that's what we mean by having a safety so we want to move quickly and safely to production and I think testing really is the most important part of that of that last word safely so when we talk about testing I think most of us are pretty familiar with the absolute basics with respect to testing a monolithic application right but I think things become more interesting when you start moving into a distributed systems world where you see that now things become distributed and now they require integration and the problem with this is that as you move to a micro services world you find that increasingly large amounts of your codebase become integration tests it's harder to validate what you're doing in this new world and so what we want to do is we want to get fast feedback the goal here is to optimize for fast feedback how quickly can i deploy an idea and get the results from that idea and then incorporate their feedback from that into the next iteration right if I'm spending all my time we for application servers to start up or if I'm spending all my time waiting for the C++ code to compile if I'm spending all my time waiting for the integration code to run on the on the in CI environment then I'm not gonna I'm not going to be moving quickly I'll be kicked out of the zone right this is a problem right we want to we want to stay perpetually in the zone want to enjoy that sense that we're moving forward we're making progress and that as we move forward we're getting good results and this by the way gets us to the next point this desire to stay in the zone does this desire to constantly have feedback as opposed to having these rivers of productivity and then long periods of just waiting and waiting and waiting the that goal that feeling that we're moving quickly that's not just a a a wish it should be a requirement it should be absolutely critical to the way you approach software and so this is why we're gonna talk about testing as well we're gonna talk about test-driven development okay today we're gonna talk about test-driven development and the idea behind test-driven development is very simple you write the tests before you write the business logic and this is important because I hope most of your writing tests are all of you writing tests who's who's writing tests okay I don't see all hands so you know maybe you have a job like me where you just write simple stuff on stage and it's mostly about the ASCII art work you know maybe I don't know but but you should be writing tests unless you're like Chuck Norris or something right you should be writing tests and it's good to write tests afterwards but it can be a bit a bit depressing it can feel like a chore so what we want is to get to a place where we can write code and deliver value into production and we can stay in the zone and so when we talk about the zone when we talk about test-driven development tester Mandela man it's very simple idea you write the test first and then you write the production code that satisfies the test so you write a broken test and then you write a little bit of production code to satisfy that test so that the test goes green this is often if you see people that are doing pair programming they call this ray Green refactoring you write somebody writes the broken code the broken test and then somebody writes the green production code that makes the test go green right and you ping-pong back and forth I write a broken unit test you write some production code to fix the unit test and then we keep going right and often you don't want to write too much test because you it'll break immediately if you write one more assertion it'll break and so you write just a little bit more production code and now it's green and then green again then you go back and eventually you get into this loop where you're constantly ping-ponging back and forth between production code and test code and this can seem very you know turbulent it's like a bumpy airplane at first right it can be it can be very confusing to work this way because you're constantly moving back and forth between two pieces of code but once you get used to it it can be very very very satisfying to work this way and the reason is because you end up doing several things by writing your tests first when you write your tests first you write code that can be tested this is important right if you write code that is full of private static classes then you're not gonna enjoy the process of testing that code it's gonna be very hard to test that code and so you won't do that because you know that you're gonna write the test that will test this thing so you're not gonna do these things that make it hard for you to get your work done and when you write code that is testable that is to say it's clean you're writing code that has parts that are testable in isolation isn't that true right a good object-oriented design is able to factor out the things that are invariant from the things that are variant write in the tests you have things that you want to test and then the things that surround it that are invariant you want to be able to isolate in order to do that you need to be able to swap out parts of the object graph you need to be able to substitute parts of the autograph well of course what what's a good way of writing code that supports this goal of clean boundaries in the codebase dependency injection right good clean testable code is dependency injection friendly code and so these things have always been friends spring and testing have always been friends it's always been a goal to make testing more productive right with spring instead of deploying this giant app server and then testing the code by running it in my EJB server or whatever you can just take a little pojo a regular java object and just poke at it in isolation so when you write tests first you write code that is cleaner it's more object-oriented it's more it has well-defined boundaries there's another benefit though forget about the technical stuff forget about architecture and code cleanliness there's a much more important point here which is that it's very satisfying to write your tests first and I mean at the brain at the level of the of the chemicals in the brain it's satisfying like I said human beings we are wired our brains are wired up to recognize and seek rewards related to achievement ok and so I'm sure some of you will appreciate this how many of you play video games ok right how many of you just I mean I love the code so that's one option how many of you just love to code you go home and you and you do it in your own free time ok right what about exercise like jogging and running and these K you know those kinds of things like ok so I'm gonna go ahead and go on a limb here and I'm gonna go ahead and say that people who said that they love jogging are lying right nobody actually likes to jog they like to get to the finish line right that's different I don't like to hurt instead I like to get to the finish line and go I did it right same thing for videogames why do we why do people spend so much time sitting in from the video game or programming right how many of you ever sat down at like 9 o'clock on the in the morning and you're working on something and you're making good progress and you're working and you're working and you're working and the next thing you know it's 6 o'clock at night or 9 o'clock or tomorrow right yeah I've had days like that or days you know multiple days right I'm sure how many of you have had something like that we just get lost in time we call that getting into the zone right it's a very important aspect of what we're trying to do here you get into the zone and the reason we get into the zone with code and with jogging and with video games is because these if we're doing it correctly we achieve flow we achieve the state of having constant small horizons to which were making slow but steady progress if we make progress towards our horizon we feel like we're making we're moving somewhere and our brain loves that it desires that so when you used when you do test-driven development you're optimizing for that dopamine hit that feeling in the brain that says oh this is awesome right you get towards when you get a green test its feedback it feels like you're getting somewhere and that's awesome because if you have a green test and you're doing test-driven development you have a green test it also means you've shipped the software you've got software that's working right compare that to the other way the other way around is you write the code first and then you write the test well if I've got the code working the first time it works it's great right it's like oh it's awesome my code works but now I have to go write the test it's a chore right I gotta uh I've gotta go write documentation uh maybe I'll do that next time you know and and that's the other thing is you you feel like well it's not that important it's already working in my machine right we've all said those famous last words and so if you have the discipline to write the test first you won't get into that situation you'll write cleaner code your write code that has tests and it's you know you can you can do things as a result of those tests you can take you can have confidence in the results of those tests you'll have the dopamine hit like I said and so and you'll be able to move faster at least it'll feel faster so all these reasons all these reasons I think are important when considering test-driven development okay so we're gonna write tests just a lot of tests today we're gonna write code using spring of course I'm we're gonna do so of course at my second favorite place on the Internet start that spring today oh this is my second favorite place on the Internet of course as always after production I love production you should love production you should go is early and often as possible bring the kids bring the family the weather is amazing it's the happiest place on earth it's better than Euro Disney but if you haven't been to production you can begin your journey here at start that's spring that IO if you need inspiration there early morning before a cup of tea or coffee start that spring that IO if your children are restless and they cannot sleep start that spring that IO and if you suffer from indigestion after a long night of alcohol abuse and PHP start that spring that I oh alright so we're gonna build a new application we're gonna call this the producer and we're just gonna bring in some reactive stuff so I'm gonna be I think most of you should be at this point familiar with some of the reactive support and spring we're gonna create a reactive application a web application that talks to a reactive MongoDB database I'm gonna use the spring cut contract verifier okay so just a bunch of stuff here we're gonna hit generate and I'm gonna open this project up in my downloads directory so ueo producer there we go and that's gonna open up IntelliJ it doesn't matter what you use what tooling use I'm gonna use IntelliJ I like intelligent but anything that supports Java 8 or greater will work just fine in this case and we're gonna build an application starting with the tests as always so let's close this out that's the wrong project goodbye to you alright now the goal here is just a right now application that writes data to the database so the first thing that we're gonna do is ignore the production code and go straight to the test you notice that when you use a spring boot and the spring initializer you get a sort of empty stub of a test and that's an interesting kind of like it's nice it's a reminder that we should be doing certain things but if I'm honest I never keep this test I always just throw this away right I want to start a different way so that's the first thing but it's nice that we have that and it's important to know that we do have that the reason we had that is because when you generate new projects on the initializer you've got test support build into the dependencies there's no checkbox I didn't have to choose that before it's just assumed in 2019 that you're going to write tests right there's no way to opt out of that so we have that by default so now I want to write a test and the first thing I'm gonna do since we know that I'm gonna write data to the database is I'm gonna use I'm gonna create an entity or an object now there's two different discussions there's two different trains of thoughts here about how you should do testing should you do inside out where you write tests for the smallest thing in the system and then work your way out to the biggest thing so from the basic object all the way to the service or the user interface right that's inside out or should you go outside in where you talk when you work from the outside the the interface the user interface or the REST API and then start writing code inwards writing the small stuff inwards and there's two different ways to think about it if you start from the inside from the bottom if you think about it the more specific thing if you work at the smallest level and then start testing outward then you can have different teams working on things right different teams can start on different services and they can start at the bottom and they can work in parallel so that's good for maybe maybe that's better for larger teams on the other hand if you know that your system has a lot of risk in the integration layer then it might be more valuable for you to start outside in because that's where the risk is that's what you want to get that's what you want to be sure about right so you can there's a it's like a whole it's just one of those things it's like tabs versus spaces and by the way the answer is of course spaces but anyway it's like tabs for versus spaces you can have it a whole long discussion about this and there's good reasons for both sides I don't care ok I'm just gonna I'm gonna start inside out only because it allows me to demonstrate certain things in a natural order at least natural for me ok so first things first I want to create an entity and I'm gonna persist in the database it's gonna be called a reservation and so I'm gonna create an object just to demonstrate that that object is valid within itself so I'm gonna create a simple test like this public void create ok so here we go throw those exceptions whoa not editor kit whatever that is here we go and it's just gonna be the objects so I'm going to say reservation R equals new reservation and you can see that we've got no entity we've got no type so I'm gonna hit create class I've got my production code good the test is green again so to speak if you have a compiler error that's a failing test okay it's red it counts as a failing test now I want to create an object with valid States I'm going to create a constructor that takes an ID in a parameter so I'll go back to my production code and create that there now I could create the constructor but I prefer to use a Lombok alright so I'm just gonna do this and there we go there's my my constructor etc now I go back to here now I want to prove that this is gonna work I'm gonna say assert equals you know re get name and I want to say Jane okay now what if I run this test you think that's gonna work I expect it will probably work all I'm proving is that if I put something in the constructor I can get it back out now this is one way to test things you know about J in and of course another thing I could do is if I have a more complex test I can use a matcher I can say let's use a matcher and matress dot you know equals ignoring case Jane okay that's a very simple thing to express I can also create my own mattress this is where this is this is one of the nice things about this is I can create my own matcher but the thing is you've got to be careful it's very tempting because the type that is expected here is a matcher and mattre is an interface it's very tempting to say well I'll just create my own matcher you know it's gonna happen if you create your own metro since you didn't read the documentation it it's a I think they're trying to communicate I think they're trying to tell us something so so don't do don't do that very clearly okay so instead you get a new bass masher and the bass matcher just it's just a very simple interface and you know you may not have to do this okay that's fine there's a bass matcher and the idea is that you say okay given an object of type object I'm gonna say item dot you know string whoops string item dot care at 0 equals or better yet we can do is upper case there we go character is upper case so I'm saying given a string the first name the name should be upper case okay and if there's an error now I have a place to you know the the name should be valid upper case and I should have written some business logic here to enforce that but you get the idea right now I can describe a complex thing and the better part is I can actually extract this out into a separate class and other people who have the same test scenario can reuse this rather than re-encoding this every single time we want to write a test so very simple example here we go run and now the message is also built-in okay good stuff okay now another thing is you can use a search a I like assert J so here I say assert shouldn't true are e dot get name and I get assert true asserted that thank you sir cert that that or not search [Music] sergej that gee-whiz is equal to ignoring case Jane okay so now I get this nice type save completions so a lot of you get all this stuff for free on the classpath already off the bat we've got some basic stuff that we can use now the next thing that I want to test is my ability to persist this objects into the database so I'm gonna create a reservation entity test I'm going to persist this object into MongoDB in order to persist that I'm gonna I'm gonna prove that the mapping is correct okay so I'm going to create an entity test to prove that and I'll create a reservation I'll say reservation R equals new reservation and I'll pass a null for the ID and then Jane for the name and I want to prove that I can talk to MongoDB so I'm going to inject a auto wired reactive MongoDB template okay and I'm I didn't write them on go to template I know that that works it's a layer below now the thing is I need to get this from somewhere and spring already knows how to create it so I could bring in spring but I don't want to test I don't want to launch all of my spring application just to have that instead I'm gonna use a test slice I'll say let's use a spring render to launch this with spring and then I'm gonna use data test so this is gonna tell spring hey spring don't bother using all the auto configuration just use the auto configuration related to the stuff in the database right so MongoDB in this case Andy will do a great job talking about this kind of stuff that test slices in great detail later but what we need to know is I'm going to say template dot save reservation and that's gonna give me a result now in reactive programming you're given these things called publishers mano is a kind of publisher publishers are asynchronous okay so testing becomes very difficult when you do asynchronous reactive data because you don't have the data you have a promise that the data will come eventually and so there's a nice abstraction a nice API in reactor which is our reactive API to allow us to poke at or assert things about the results so here I can say given this publisher let's assert that the reservation that comes back has a name that's equal to Jane and let's assert that the ID is not know right so we sent to no but when it comes back from the database it should be not know alright so there's the reactive testing mechanism there okay so let's see if that works now this is we think I think this is probably gonna work right if I had done something different maybe I had named my entity differently you know for example this if this wasn't named ID Mikey then I would have had to have a tie Dean I want to make sure that that works correctly right but by convention that key the the field named ideas named correctly and also if I have a different document you know I could do something here where I could actually look at the annotation and see if that's working correctly so that worked MongoDB is clearly working and it's fast now what about a custom query right queries are strings there's something you want to test so I'm going to test my repository I'm going to create a repository but I'm not gonna I don't have to test my repository my most of my repository will work just fine right that's the spring data team does a great job with testing the implementations for the repositories the only time you really need to test it is when you're using a custom query so I'm gonna inject my reservation repository here okay and I don't have that yeah that's production code so we'll create that and it's just gonna be an interface so interface extends reactive crud repository managing reservations there you go okay and I want to create a custom query that says given a request for all the reservations by name where the name equals name returned those results it's gonna return a reactive stream that contains all the reservations whose reservation name property equals name so there's my repository and I want to prove that that works so what I'm gonna do is I'm going to insert I'm gonna delete everything in the database I'm going to write four records then I'm going to query by name okay and assert count so that's my logic here so I'm gonna say flux not just a b c c dot map name equals new reservation passing a null passing in that and what a flat map that i'm gonna save each are into the database like this passing in there and when that happens I'm gonna say find all the in the database that matches this string and before I do that I want to say this not repository don't delete all and then many okay so I'm stringing together these reactive streams here I'm using the operators in reactor to do that so there we go there's my my reactive stream and I want to poke at that result I'm going to say step verifier that create reservation flux expect next count equals two right I should have two results that are of type C or name C so let's run this code and see what we get all right seems to be okay sometimes though just to be healthy just to be sure it's always good to ask yourself is my test broken right and as a result am I getting the wrong results so sometimes you can just test the negative you you break the tests so that it should show a failure just so that you know that everything is working right sometimes I write so many tests and it always just works I'm like that can't be right I can't be that good right no way so it's always good just especially late at night always break the test to make sure you're not doing something crazy that's that's masking a real problem so that broke which is what we expect okay so we're gonna put it back to two and we know things fine now I want to move up one more layer I'm going to create an at reservation HTTP API okay reservation HTTP test and this is gonna be something that's gonna test the web tier so I'm gonna say query or no get all reservations okay like so there's that okay and I'm gonna use run with but I'm gonna be testing their whipped here not the data tier right I don't want to test the MongoDB stuff we already did that that's already working so instead I'm gonna use a web slice and this slice is gonna activate the stuff related to the web tier and in order to test my reactive HTTP API I'm gonna inject the web test client okay and here this is going to make a get call to reservations and I'm going to expect the data to come back I'm going to expect that the status is a HTTP 200 and expect that the body or that the headers has a content type that's compatible with media type application JSON and I'm gonna expect that the body has a JSON path so how many of you know JSON path Jason path is like a query language for drilling down into the document structure of a JSON object like exit XPath from years ago okay so is equal to Jane all right so I'm gonna say when the first result comes back I'm expecting that first result to be have a name equal to Jane and I'll have the second one equal to Jo okay so there we go there's my two records and that's my my simple test and now of course what's wrong with this code first of all is that gonna work of course not I haven't written any production code yet so let's do that I can see already there's one important piece okay okay so this is a configuration class great and I'm gonna use a functional reactive endpoint here I'll say server response and the server response is just gonna produce a simple come on it's trying to tell me to use Kotlin did you see that those were Kotlin types so route dot build I'm gonna say get forward slash reservations new handler function okay so my job is going to be to inject the reservation repository here are our I'll say server response that okay that body RR that find all reservation class ok so there's that replace it with a lambda static imports very simple HTTP endpoint right not a big deal now I want to test this in order to test it of course I need to bring in the config class so I'm going to use the the value and it'll be reservation HTTP test etc is that gonna work no of course not there's a couple of obvious reasons first of all did did you see me create two records with Jane and Joe in it no and even if I did it doesn't look like we even got that far does it look at this well first of all it says 404 not found that's a totally different issue what do you want me to do this I think it wants me to do that let's try that yep okay so that didn't work import it's already present well value is equal to that oh it's reservation HTTP config ha HTTP or did I even call it reservation I wrote the production code I called it test I've got tests on the brain friends oh that's so confusing okay so here we go configuration names matter good stuff okay it's all and think about is how I'm gonna test this code okay so let's try that again this time with less can feel it's did I just oh no no it's configuration yeah yeah all right HP config very good okay this time with more gusto come on no runnable methods fine you're confused I get it reservation it should be test how are we doing okay so there we go we're requiring a beating of type reservation repository so again because it's a web slice because it's using the web flex slice the stuff in the database layer it doesn't exist it's not it doesn't get created so I need to inject I need to create a mock version of the repository like that now I could create my own implementation you know that kind of thing but that would be a lot of work so what I would like to do is to use mockito I would mock out the object now I have spring spring knows about all the objects I want spring to use a mock object and replace the thing that I want with that mock so that'll be fine and I can do mock bean okay mock bean tells spring find this object in the application context if it exists and create a mock version of it if it if it if it exists replace the one that's already there if it exists and if it doesn't exist then add this mockito mock version of it and because it's a Makita mock remember a mock is just an object that has empty you know default values no false 0 etc right but what I want is not a mock what I want is a stub a stub is a mock with some pre-programmed values with pre-programmed behavior so I'm gonna say mockito dot when this dot repository to find all then return flux dot just new reservation one Jane to what did I say Jo okay there's my pre-programmed results okay and let's see if that works looks good okay so that's good now I want to build a client I'm gonna go back here actually I'll just go ahead and get rid of some of this stuff I don't need MongoDB don't need this I do want a consumer and I want the verifier okay let's do burn or rather okay so I hit generate and what I want to do now is I want to build a consumer to talk to that API and so what I'm gonna do is I'm going to open up this application in my IDE consumer okay I'll go to my code and the consumer side I'm gonna create a micro service you know client basically something that people who want to use this can use to talk to my API so I'll say reservation client test and the production cut in the end the test code and it'll be run with spring run or class it'll be a spring boot test and what I'm gonna do is I'm going to make a call to my micro service I'm gonna provide a client that people can use to make calls to the micro service I'm gonna consume that micro service so in order to do that I'll inject a reference to my reservation client which of course we haven't written yet halfway so here we are and I'll add the client code there and this is going to be a spring bean of course and it'll have an endpoint you know like this so we you know we can see what we're gonna do here so this don't client thought get all reservations and it'll be a publisher of reservation res equals that and obviously I need this type on the class path so I'll create this class here this production code it's a DTO right so and ID and I want at data at all arcs constructor at norc's constructor and I want this method okay so there we go in order to create that I'm going to use the reactive web clients I'm gonna inject that I could create a constructor but I like Lombok Lombok it makes it easy so I can just inject it by a via constructor which gets written for me I'll say this client get dot URI HTTP localhost 8080 forward slash reservations that retrieved that body to explore Ezzor vation doc class there we go simple right now in my test code I want to prove I want to assert things about the data I'll say when the data comes back I want to let's say that I look at the data I'm gonna use a predicate here I'll use this predicate that we you know you can imagine writing yourself it's very simple so I'm gonna say a list of string names equals arrays dot as list Jane and Joe okay so there's my list and I'm gonna say when the data comes in reservation get name I'm gonna say names dot contains this so it should be one of those valid names verify complete okay very simple and I'm gonna I have to do this twice because there's two different results they're gonna come back each of which will match one of these names so now let's see if I run this code have I done everything I need to do I think we're gonna it's gonna fail but I don't know why why I have an idea why but I still don't know why I let's see what happens if it's fails okay first failure okay reservation client needs a beam of type web client fair enough so I'm going to factory a beam of the reactive web client all right web client and I'm gonna inject the web client builder because spring will pre configure that with anything I want it to configure like security and client-side load balancing and all that kind of stuff and then I just provide the fine I just call build and get the final resulting thing now what's gonna happen let's see what happens if we run this test one more time Oh uh-oh we've got a problem don't we Jabba net connection exception it's tried to call that service but of course the service isn't running this is a bit of a problem isn't it we've moved to micro services I've got producer and consumer client and service and now one is calling the other over the network I don't I don't have that service running now I don't know I'm here we're here in Europe and and Europe things are a little different than where I'm from I I live in San Francisco and the Bay Area in the States it's okay in in in San Francisco though a very friendly polite greeting for somebody who comes to work with your organization in your organization as a new employee a very polite thing to say to them is hello or welcome or please you know have a seat here join us you know that's a very welcoming inclusive thing to say it's very polite a very vulgar thing very very vulgar okay one of the things that you can say that people sometimes do and it's very I've seen people leave companies because of this it's just very rude one thing that people sometimes say that you should never say right is welcome please come in and deploy this kubernetes cluster so you can test anything right nothing is more insulting or rude nothing says I don't like you more than having to deploy a whole production system just to be able to like I can't run slack do you think I can run your kubernetes cluster no no no no no no no so that's not an option not an option so instead what I want is some way to mock out this other service the service that I'm making a call to okay one way to do that is to use wire mock so I can say on my test auto-configure wire mach port equals 8080 and here I'll say wire mock dot stub for wire mock dot get wire mock this is not our API we didn't build this dot with we'll return wire mock a response dot with body and we'll create that in a second with header HTTP headers dot app content type equals media type application Jason value with status 200 and and that's and then we need the body so we'll provide the body there and the body will be a string and now I'm gonna create I'm gonna just use auto configure JSON right so auto configure JSON I'm gonna inject a Jackson object mapper it's gonna be a little longer so we're gonna inject an object mapper here and we're gonna use the object mapper to create instances of our records okay and in order to do that I'm gonna pass in two records here new reservation one Jane new reservation to Joe okay so I've got some JSON there JSON and I'm gonna use the JSON like this and do you notice that I call it JSON because JSON is there's two there's JSON and then there's Jason Jason is somebody's name right that's a very interesting date hennifer I think crazy that joke from the Python community a very funny guy anywho Doe so there we go I'm gonna now run this okay and expectation matches reservation oh I guess it's wrong wrong way around isn't it ah yeah one two there we go probably probably better if I just fix that so it's consistent whoa okay so now one - always good to be consistent okay three let's try that again one more time so it looks like it's green right thing is a green here and things are green and the producer side everything should be great and yet there's a bit of a bit of a wrinkle isn't there bit of a fly in the ointment isn't there because if you look at this you see I've got this field called reservation name but I don't have a reservation name on the producer side on the server right I just have name and that's a very dangerous situation to be in we've got two different teams working on different things and one side is working and everything's green on their side and the other side everything's green as well and there's an end so it looks like we're all everything's gonna be fine let's just let's just deploy it it'll be fine right this isn't PHP we can't just discover the stuff in production right we need to do better and so one thing that people do is they create integration tests that's where the you know you start deploying companies clusters and of course you can't run it on your local machine so you have a shared server or services or cloud cluster that your organization uses for integration testing and then of course you have to wait for people to be free or to be done with it or you can spit up your own in which case you've got this crazy cost no no gets to be very painful very quickly right especially if everybody has an isolated copy of their whole multi-tenant distributed cloud so I want to get fast feedback but I want to avoid spending too much time in the integration test layer remember when you have a testing pyramid 80% of it should be very fast feedback yet like unit tests and mock tests like this and then at the top you should have slower but more exhaustive and more comprehensive integration tests and you know end to end tests and things like that smoke tests so we want fast feedback and I don't want to give up that productive productivity but I've discovered a problem here if this was a monolith and I changed the field in the monolith the compiler would tell me but I don't have a compiler for my Network API and so what I want is some way to capture that and then define that incompatibility in my tests a good way to do this is with contracts consumer-driven contracts so what I'm gonna do is I'm gonna go back to my producer here and I'm gonna use spring cloud contract to describe how this API should work a contract a contract a contract is a thing that that a gets turned into a test right so I'm gonna say should return all reservations groovy and it's just gonna be a thing that I've got a map location I'm gonna say contract dot make and I'll say description reservations so I'm using goofy here too as a DSL I'm describing how data should be discussed should look basically how an interaction with my API should look I'm saying when somebody makes a request to the server to the producer at for such reservations using HTTP method get' then I expect to have status 200 and expect to have headers including the content type and I want the content type to be equal to application Jason value right and I want the body to have the following shape okay so that body is going to be a JSON structure and I can use a string here if I wanted to but I have this nice object literal syntax and goofy that looks kind of like JSON so I can just do that instead so here we go all right and then Joe and voila okay so there's my there's my contract now a contract is only useful if something enforces it remember this is going to be compiled into a test it's gonna be transpiled into a unit test okay so I'm gonna use a plug-in to do that work and to force this test to get executed every single time I build my producer desk misc depths contract spring cloud contract depths okay I'm gonna go here to my build section build plugin and I'm adding the spring code contract maven plugin and I'm also telling this plugin whenever you create a test out of my contracts extend that test from the base class and the reason that we need to do that is because oh sorry this goes in the producer side not the consumer the reason we want to do that is because we want to know that what we are doing is actually going to have the set up logic that we saw before so did I have the contract here no source test wheeze I didn't come okay here we go put that here so a sourced test resources contracts there we go okay so I've got now in the producer side on the server side I've got this contract it's going to extend this class called base class which I've configured in the plugin here base class and I need to create that here in the test folder so base class and this base class is just going to be a place where I do the same kind of set up logic that you saw me do before but it's gonna be available for this generated test instead so it'll be a run with spring runner class it'll be a spring boot test sure it'll have some properties including server type port equals 0 and I'm going to say a web environment equals random okay and we're gonna say that the config class is going to use the reservation HTTP configuration class among other things right or actually we don't even need that but that'll that'll work okay and then we're going to do as we did before we use mock bean to create the reservation repository and we're going to inject the local server port and we're gonna do some basic setup so here's my HTTP test okay so same as before put that in there and we're gonna say rest assured that base URI equals HTTP localhost plus this dot port so I'm now configuring all base logic all code that extends that did I miss anything let's make sure I didn't miss anything we don't have a sorry oh yeah thank you whoo all right good stuff I'm missing something kind of important here I feel like and so just to be a hundred percent sure I'm gonna cheat a little bit base class you have a import okay I didn't need that okay fair enough fair enough I had that I had that and I was like I'm not sure if that's real or if I need that but you know if I did it before it's probably a reason okay so now let's try it let's go to the producer downloads producer may even clean install actually before I do that dot m2 repository calm example may even clean install take some coffee okay what do we got stubs could not be found source test resources contracts okay fingers crossed eyes crossed good stuff okay so we can see it compiled everything is fine you can see that installed the jar and the Maven palm that xml and most importantly it installed this thing called stubs and the stubs contains a file that our consumer will consume you can see that it created a fake test for us right so verifier i guess let me see it like this verifier test dot java okay well find CD target find it's in the when the producer is still right so in the producer okay contract verifier test okay so contract verifier okay Yahveh good and if we look at this file on the producer side you can see that that's actually just a unit test that was created for us by the compiler and it has the same specification as our contract it says it's gonna be 200 applications Azon it'll have an ID equal to one a name equal to jane another one named joe and so on and that's been communicated it's now in the maven repository it could be Nexus or artifactory or whatever but it's in my local m2 repository now on the client side I can get rid of this okay I'm gonna get rid of wire Mach because I don't need wire luck because it was it was testing the wrong thing now I'm gonna use the stub Runner and here I'm gonna tell the stub Runner go to maven find the producer artifact find the latest version of it plus means latest version and run the resulting definition on port 8080 and also I want you to look in local class path not in artifactory not in in your class path but in the local m2 repository and if we do everything correctly and if the contract has the same shape as our client API expects then this should work let's see if it catches our bug right let's see so there is the data we got names back expect next matches it says reservation name equals null so it failed right we know why it failed let's go fix this our client-side should say name rebuild this now okay and we have to fix that get name get name and we start the test cool all right my friends so hopefully you got a feeling for what it means to write code that is testable to go fast to get to move to a place where we can use test-driven development to isolate parts of our system including distributed systems including distributed services there are talks on spring cloud contract there's talks on testing with Andy Wilkinson there are talks on a lot of this stuff in-depth interactive programming of course so go see those thank you so much for coming I appreciate it [Music]
Info
Channel: Spring I/O
Views: 15,164
Rating: 4.9000001 out of 5
Keywords: springio19, TDD, testing, reactive, spring boot
Id: E87XhgYBM-Y
Channel Id: undefined
Length: 54min 15sec (3255 seconds)
Published: Wed Jun 05 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.