Spring Tips: Bootiful Testing

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
and welcome to another installment of spring tips in this installment we're going to look at how to do testing and TDD we look at it in terms of of course the features in spring boot so we'll start with a a new project that starts out spring that I oh now we're going to follow the three rules of test-driven development which I've seen best explained by Bob Martin Bob Martin says that there are three rules when doing test-driven development so three rules of TDD Bob Martin and you find this can be a useful blog and that it's the first rule is you're not allowed to write any production code unless it is to make a failing unit test pass the second rule is that you're not allowed to write any more of a unit test than is sufficient to fail and compilation failures are failures and third and finally you are not allowed to write any more production code that is sufficient to pass the one failing in test so basically don't move too far ahead right you're gonna keep in a cycle keep in a loop as you write a test you should write the production code that makes a test pass and then once you've written just enough to make that test pass you go back and write more tests and you don't write any more tests than to make the production code fail right so it keeps you in a cycle where you're constantly going back and forth between production code and test code and test code and production code and you know ideally you shouldn't spend more than say 30 seconds long on any one particular thing right you know you're gonna constantly be in this loop the result of doing this the the effect of moving to this are many fold first you end up writing software that is testable right that's the most important implication of this by writing software is testable you you have to force the code to be to be compartmentalized to be easily marginalized right the the boundaries the scenes of collaborating objects have to be clearly demarcated because that's the only way you can mock out different layers of the application right you can mock out these pieces that you don't care about by substituting dependencies with mocks and so of course that lends itself to inversion of control and dependency injection the very basis of all things spring another benefit of moving to this architecture to this approach of course is that when you write software you getting a dopamine hit there's a dopamine hit associated with achieving or accomplishing something when you write software that is working and then you write the test it's not very satisfactory is it it feels like you've had to do a chore you went back in with the documentation and you went back and you wrote the the tests but you already know works and if you're pressed for time you might very well feel like you can skip that particular step right because it's already working after all that's a that's a bad mindset to get into so it's much better to write the code or the test first and then write the satisfying test that satisfy that rather then write the satisfying production code that's satisfy the test so now you've got two for one you've got working code and you've got your dopamine hit associated with getting your code to go green when you write code in this way all your code starts with a test so you know you've got good coverage because you couldn't have written a line of pression code without having written The Associated test so you now have a test suite a harness that you can you can trust you can refactor with impunity you know that you've covered everything and so when you make changes you know that the harness will capture everything right so you get a lot of benefits from this approach I think it's worth doing we at pivotal of course at pivotal labs we do a a lot of pair programming right pair programming has a lot of benefits so there's like I could go on forever but one of the big benefits is this pair programming approach when you're doing testing we do red green refactoring so somebody writes the test to that fail so we write the test that fails then that the pair writes the code that makes set that test pass right so you go back and forth and you can send them you can switch roles of course right one person can write the production code and the other one day.the the test and you you get two sides two branes focus on the same problem and you don't feel like you're stepping in or stepping over each other's feet you're actually writing a specification which is the test and then you're implementing that specification right so a lot of benefits are moving to this approach I definitely recommend you look into it further and we're going to do that today we're gonna look at test-driven development but we're not going to do we're going to linger too long on the on the notion of testing test driven development were gonna look at spring support for testing so let's create a new project as we normally do and here it starts a spring that I owe and whilst will call this bootiful testing and when I used to bring support from building web applications we use JPA we use h2 Islam buck and we'll hit generate now I'm not you know it doesn't really matter what version of spring boot you use all the stuff that doesn't work has been in spring since or rather spring boots since when dot four so we're on firm ground whatever version we use these days so we'll open up the the release all right now we have a new application and we could start here but again when I write the tests first so a community command here is control shift T in the telogen it takes you to the corresponding test in this case we don't actually want that corresponding test do we want to start with simple saying that you know we can build into the system so the first thing we're gonna test is our basic entity a reservation entity we're not even going to talk about it in terms of entity you wouldn't talk about it in terms of the object we're gonna test the soundness of this entity so when okay a reservation test and there in will test the creation of the object right so a test public void creation right and I'll I will learn to spell having R and in my object in my test rather I want to confirm that when I create an object of type reservation and I give it some data in the constructor that it does the right thing so that's create a record of that form already our test has failed rather so we need to actually create this production class we can hit alt enter that'll create it in the corresponding package I want to provide values in the constructor obviously that needs to be done as well now I could create the constructor myself I could say reservation long L you know and then string name right but what I want to do is actually take a little short shortcut here I'm going to use the Lombok annotations and you know I'm going to take for granted that in this case the state of my object is the same as the structure of my alert so all create was to field to your private long ID and private string reservation name like so string reservation name a voila so there we are we have two fields by having the at all args constructor and the data annotation I get a constructor that will you know work here and of course now I can say art I get ID get reservation name etc which lets me write some test so I can say assert equals or get ID and I want to say that it's equal to 1 l right so I'm gonna I'm gonna up cast or promote correct that that a unboxed integer or long rather into a java.lang long there and i'll sir that it's working so we can run this test now so let's see run that code some teams ever worked good this is one way to do it of course we could do that we can use the basic j init methods that have been in jail since you know forever and a day we could also use the assert that variance right so asserted that and this expects a object of type matcher now for reasons i don't really understand or even care about because it's really gross you're not supposed to use matcher right you see that matcher has this great method that says don't implement matter which of course begs a question why did you put that there in the first place I suspect that given the availability of default methods in Java 8 these days they wouldn't have made that same decision and for other purposes we just need to extend based metric so you can create your own custom matress and the nice thing about this approach is that these matters can be packaged up you can package them up and distribute them so now you can reuse complex tests complex assertions and function on be there and for our purposes though we're just going to use some pre-built mattress that more or less mirror to the stuff that we would have gotten with basic Dana a certain asterisk method these are from a project library called hamcrest and there's a number of different matches here we can say that we're gonna assert that the ID is equal to 1 L right so there's that now let's run that code again okay so that seems to be okay great let's take it one step further we can use a library called a certain J you see the the the test here has a generic constraint but otherwise it's not really you know there's no guidance as to what is an appropriate matcher to put here given the type here given the parameter on the left so the matcher on the right isn't informed necessarily or driven by the parameter on the Left where as I've used the assertions library from assert J you can say R get ID is equal to 1 l right it kind of autocompletes itself and so I prefer that right get reservation name see that works but I can see that it also says it's not blank and I can say that is equal to Jane so I get this a very very convenient sort of type safe matching functionally this test functionality and the test basically writes itself let's break the test here let's say that it's equal to Janet and see what happens right the error message is very useful here right so you get all that functionality very quickly you know all that is nice let's close this now I have just use the assertions library that's fine but it's also interesting that there's a BDD assertions class in a certain day which gives you basically the same functionality but with a then keyword so you can say given when then using the BD style of testing and that's worth your consideration as well if you're doing BDD which you should be I mean there's a use case for that right so I never say never but for our purposes I think it surprises delay things as they are here now the next thing that we're gonna want to test is the the JPA mapping right so we've got a class but it's not entity we just know that structurally it'll do the right thing when I give it a constructor argument it'll pop out give it a given an invocation to an accessor right so that that's working we can say a reservation JPA test okay and there's this and we can say test public void and what we're going to do is test the mapping right so I want to test the mapping here of the GOP entity when it interacts with the Jaypee and any manager well I could you know mock or I could configure all of JPA and the data source and all that stuff but I forgot spring and spring already knows about all that wiring so why would I recreate that so I can actually use the at run with spring runner class and the spring runner is a is a test runner that we provide in in spring Boone and in spring framework and it's a test runner that is you know it's a mechanism that Gena itself exposes right this is a thing that's from j-unit which you can use to tell spring - you can tell Gina - to involve spring in the execution of your tests and to make available collaborating objects and so on so that's very very convenient you need to give this more information that way this will say activate the machinery required to run spring but you know what would a plication context what configuration class right a lots of things here they are required there are a lot of things that are required beyond the recognition that we're going to be involving spring somehow so we could say at spring boot test and you know there's a classes parameter or an attribute that is expecting the configuration class that we want to run we could certainly do that that's certainly one option the problem with this of course is that the the default behavior of the spring boot test annotation is to load the entire application context and all we're trying to do is to test our interaction with JPA just the entitymanager mapping right just the interaction between the energy manager and the annotations on my entity so I don't want to startup Tomcat just to test that I'd much prefer to instead start up a slice of the of the application context related to persistence and JPA and run that and then in isolation test the things there that we care about and then leave out the rest right and that was easier to do before in the world of manual configuration of everything with you know different spring application context xml's and configuration classes you could certainly slice and dice your application in terms of layers and that was a little bit easier to do because they'd have multiple configuration files and you could in your tests instantiate just one application context XML and ignore the rest that was certainly an option I don't want to go back to that though I much rather let spring boot write the configuration for me I'm very happy with that and what what little is left i I test myself that said I don't want to run the whole application just to test a piece of functionality so spring boot 1.4 has this concept of a test slice so instead of using at spring boot test which is like it's it's a slice of the whole it's the slice that is equal to the whole pie basically instead of using that I'm going to use a data JPA test class and here that can provide a number of different things but what this is going to say is first of all it's gonna do it's going to do three things it's gonna register it's going to remove the outer configuration that is there by default it's going to basically set to zero all the configuration classes that spring but would normally run then it's going to provide some configuration classes that should be run then it's going to contribute things that we would need to effectively test this kind of stuff so including stuff that you wouldn't otherwise get in the auto configuration for the production code right this is stuff that is valuable only in a test context and also part of that it'll set some defaults it'll set for example that that the test methods here are transactional right so you can see here that there's a lot of a lot of stuff happening here it says this is going to be it's going to override auto configuration it's going to exclude certain classes from the application context we're telling spring don't bother with certain types just ignore them pretend that they aren't registered we don't want them in this code base anyway and we're going to tell it to auto configure certain things and these annotations also have attributes that are mapped sometimes to properties so for example let's see going back here there we go we can see that the cache provider attribute for the auto configure cache is mapped to the spring boot property file you know value called spring that cache that type so if you in your test folder have an application that yeah mall or application are properties that has spring that cache that type equals whatever it'll be set on this annotation and used to inform the behavior of this test only in the test code right so you get a lot of be a lot of nice benefits here um you'll also notice that we have this we looked at this before we had this override Auto configuration this tells spring spring boot rather that oh and by the way we also have other properties here I just saw that spring JPSO shows equal is equal to true right you could override that but it's equal to true by default so you you'll see later on when we use jpay that it's kind of show us a DDL that's used to create the entities um one thing that's nice about this overwriting Auto configuration is that we're telling spring to set you know to remove all the auto configuration and then let us recreate from zero the configuration required to do JPA and to test in that world we can we can provide our own auto configuration as well so we can say you know spring that factory's text file in the meta introductory we can create our own text file that has the name of this annotation org spring frame boot test Orem JPA did it APA test equals and then our own auto configurations so that whenever we use day to day pay test our own auto configurations are involved as well going beyond what's happening here and this whole mechanism by the way of course is completely reusable you can create your own tests Isis perhaps you've got some aspect of the application that should be tested in isolation independent of the default configurations in spring boot and you can absolutely do that you can package it up as a test slice annotation like this and and then stupid that and that way there's a go to usable mechanism for instantiating just the bits that you care about when your test let's see what else do we care about here huh we have excluded we have the in Harrison we've got the auto configuration ah what do we have Auto configure in here this stuff makes a lot of sense this is just basic spring data GPA of course as well that's gonna work as we expect here's the cash support but this is gonna create a test database so in my build in my build I have a spring boot starter data JPA and it has a h2 database here to work with right this is a default convenience you see me use this a lot in various spring boot videos I use h2 because it's embedded it's in memory it's to stand it up I don't have to configure a data source I don't have to have anything running my local machine which by extension means that you don't either if you want to play with the code but the the underlying assumption of course always is that you're probably not gonna use that in production you're gonna go to application of properties and say spring that data source that password a user name or URL or whatever and point it to something real like a my sequel or Postgres or Oracle or whatever right sequel server you know anything these days and you can certainly do that and you probably will all right that said that's not gonna be what you test against is it I don't expect you to test your your application against your production Oracle database that would be crazy so instead you're gonna want to mock out or at least point to a different data source and you can do that by overwriting the properties but that gets to be very tedious you have to now involve you know your Oracle a Oracle installation somewhere just to run your unit tests and that's even more grading because we don't care about oral color at this point all we're trying to prove is that when I map my entity that jpay knows to knows what to do with it that's it that's all we're trying to prove here we don't need the database for that you know we we want some database we want something there but really we're just trying to prove it we've got the right mapping annotations in place so that's a layer below us that we don't care about and I want to mock it out already someone I'm not care about and that's what this the annotation is gonna do it's going to create a in-memory h2 database for us even though right now my application is using h2 I could say use post quiz this would still create an in-memory h2 database for us in the test context so my my reads and writes are all to this sort of in-memory ephemeral database it makes it really really quick we're also going to auto configure a test entity manager if you've ever dealt with a low-level JPA persistence context annotated any manager then you know that any manager is a generic friendly version of the hibernate session basically and it's very low-level and if we're trying to do things with what's testing then the the the use cases the patterns in testing are going to be different from the the interactions you might expect when you write production code using the entity manager so this creates a new object it contributes a new object that's useful only in the context of testing and that object is called guess what test enemymanager and it provides some convenient so we'll take advantage of that as well so here we go we better mapping I'm gonna auto wire the test entitymanager all right te M AR and we're going to use that test any manager willing to say this TEM and when we call persist flush and find so this is going to be a you know three four it's gonna write it's gonna write the record to our in-memory database to test you know test database that we have it's going to flush the session and then it's going to retrieve the record by its ID all in one fell swoop so we can prove exactly what we're trying to prove which is that given a new reservation oops of the following form null and chain that we get back a record that we expect so there's Jane we're gonna say assertions dot Jane dot get reservation name that is equal to Jane good and I'm gonna say get ID is not know right so there's that as well so we've got these two different methods we were going to say that research reservation name is equal to name to Jean and the ID is gonna be not know we also want to say that the ID should be greater than for example one zero right we don't it's that's a truism for a primary key here all right so let's see what that code looks like let's run that code and of course that failed because we're testing the wrong thing first all I forgot to undo the old tests I'm glad we ran that so we can fix it so let's see reservation jp8 test and that's this one reservation test we broke this earlier just to prove what would happen and I forgot to restore it all right that's this and now the mapping we're gonna test by running it instead good now that's fine now we run this which is the mapping test okay so at that broke predictably it's saying that we have an unknown entity that's fair enough so let's go back to our entity and bring in the Jaypee into the annotation and we can run that same code again mapping now breaks a different way I would expect yep no identifiers specified for the entity so at ID at generated value right there we are so let's run this one more time all right so that seems to be good and green moving up let's now test the repository so we're gonna say reservation repository test and what we're trying to do here is to prove that our custom Feiner method works right we're not going to test the repository itself we know that spring data works just fine I don't need to prove that the final method and save method and all that stuff work as expected that's been tested time and time again by innumerable many other people or we don't have to worry about that that's the benefit of building a framework but if we customize the repository and provide custom finder methods it can be useful to validate that that's working as we expect it to so let's do the same thing as we did before public void we're going to test define by reservation name method and this of course you is the spring runner so sorry as run with spring runner dot class and that'll be of course a data jjjp test that we aren't good and we're going to inject our reservation repository all right here we are so that repository doesn't exist of course so we'll create that otherwise we have a failing test okay and we're gonna use the repository when I say this not repository us save of course we can't save a new record until we have the save method so we're already at our first failing test let's fix that all right I'll then to recreate this method we could do that but of course we we both know that a spring data will do that for us so we're gonna say @jp a repository reservation managing entities it took long there we are and that'll give us a saved record in the database and we should be able to based on that use our repository to find by reservation name passing in Jane and of course this method doesn't exist and that's a far more problematic one we have to actually implement that one that's our that's the thing that's gonna be under test right so here we are reservation string there we are named airway there's a custom finder method our test now should we turn a collection of you know by reservation name results we can say assertions that by reservation name that size is equal to 1 right remember we're using a test database so there's no more than one record in the database and we want to say that the iterator dot next dot get ID is greater than 0 and we're going to recreate the iterator so we'll get the same record back again and we're gonna get now the reservation name I'm going to say that that is equal to Jane all right so let's now try running that one find reservation name good stuff all right so we now have a test that proves that our interaction with the repositories working as expected we can see by the way of course the DDL I mentioned that that would happen earlier we can see the DDL reflect on the console here and so we've tested in there now let's build a weapon API let's test the rest a pack that we're gonna build first of course I want to say reservation rest controller test and here instead of using the test life supporting data access we're going to use the web MVC test and test slice so I'm going to use that and the spring runner dot class all right and the at web MVC test couldn't figures for us the cash support the web MVC support mock NB c-- and exclude certain objects that we don't care about including those by the way related to persistence so this isn't the right tier the test persistence logic we've already done that so we want to take that for granted so we're going to take advantage of the pre-configured mock NB C client right that's what's been configured for us and I'm gonna create a new test public void get reservation so we're going to prove that given a request that we get the right response so mark mu C perform Muk mu C result mattress dot status that is okay so I'm sorry we have to do this correctly result request builders that get reservations there we go and expect that it's okay right now we add the exception there there's our simplest test that's a as little as I can write to fail all right now the amok mu C client here is a sort of an engineered agreement between the rest framework the framework errs spring MVC of course and the the the the test code here we're not actually creating real HTTP calls there's no socket open here with HTTP payloads going across the wire what's happening is we're making requests through the service from the client but the client is talking to spring NB C itself not - Tomcat or something like Tomcat we're not there's no server socket listening for requests so this is going straight to spring MVC spring MVC is processing the request as though or an actual request from the web it's going through all of the processing phases going through the the HTTP messaging translation and the message converters the validation the you know all that stuff to do with processing of a request after this Tomcat has turned it into a httpservletrequest right all that stuff is being done for us just as though we had actually issued the request but the benefit we're not starting up Tomcat because we don't care about Tomcat we know Tomcat works so let's let's get this basic test to work let's go ahead and go to our reservation controller again very convenient we can go here and reservation rest control the reservation rest controller and we'll say at test ok oh sorry we're not going to say at get mapping and it's gonna be a four slash reservations and point and all we're gonna do is return an empty array list right so collections dot empty and we'll say at rest controller there we are so we'll go back to our test here and run this again good that passed let's do something more substantial we'll say also that we expect the content the content type to be equal to application JSON like so we start this all right that seems to be working good let's now do something that's just a bit more substantial than that we'll actually poke at the result that comes back so mark MVC result mat shirts dot JSON path and we're going to say at that zero dot ID is going to be equal to one l and we're gonna say that the reservation name again I'm writing two tests instead of one I shouldn't do that it's going to be equal to Jane and we'll run this again and that should fail substantially right all right so we know that there is no value at that JSON path expression here we're using the J way jason path match to write sets that supporter for us we don't have to import anything that just works and we need to actually configure we need to respond with data in the response and we can do that of course by injecting the repository so it's a private final reservation repository injecting that into the constructor and then here we'll say return this type reservation repository at find all now if I go back to this test what do you think is gonna happen it's not gonna work we know that right All Right see that it says no such beam definition exception no qualifying being of type comm bootiful testing reservation for paws are available that's because it's been excluded from the application context and that's by design the last thing that we want to test is our database layer we're not trying to test that we know that that works we've already done an extensive amount of testing and that layer and below it to prove that that works so instead what we need to do is to mock out the reservation repository have to tell our risk controller to use the reservation repository but we're gonna give it a a fake version of that object you know we know that's an interface so we can provide any implementation that we want and this would be possible you could contribute your own custom configuration class right I could say you know let me see can I do that I can say uh I guess I can't I hope if you're using spring boot test you can provide own custom configuration class I think you can actually say just my config and make this a configuration class and then provide your own being of type of reservation repository that's I guess that's an option let's see what happens if we do that right so mockito dot mock reservation repository at class and this will give you a response and what we're gonna say mockito dot dot when mock dot find all dot then return raise dot pass list new reservation and it'll be one l and Jane right I think that's I think that'll work so let's see return mock so that usually works I know you can do something like that with the spring loot test framework support does that work all right the content-type has not been set so we're losing for different reasons let's try fixing the risk controller here produces media type application JSON utf-8 value okay content type is not set well it doesn't make sense I'll bet that this is to do with my clumsy mocking of the object here and that's okay that's actually just fine because this isn't the right way to solve it anyway is my point what we need to do is to mark out that object and replace it with a mock bean right so we could attempt to carve it out ourselves and provide a custom configuration class and that that as you see it it's I'm not sure if even possible in this particular configuration then either way it's the wrong way to do it instead we can take advantage of an object called mark bean orientation rather called mock bean what this does is it provides you with a it's hell spring that this type should be contribute to the application context if it doesn't exist and if it does exist it should be replaced with this mock object so here going to contribute a bean of type reservation repository that's gonna be a mock and house I'm not going to be created well it's gonna use mockito just like I tried to do a minute ago using that custom clumsy configuration class this mock well you know now when spring starts up it's gonna inject this reservation repository this will work it'll get as far as a constructor this pointer will be to a mock object and everything will be will be just fine until we get to this line we're trying return the data from the final method that's not gonna work for of course because a mock by definition is an empty object to provide stubs it provides sorry it provides default values Knolls and zeros and false right it just refines it provides as little as it can and it's basically an empty husk it's gonna it'll help us get past the construction phase here but if we need to do anything more than have a stand-in just to take up space and and get past an occasional a certain old kolben that's not gonna work what we need is a stub a stub is a mock that has pre-programmed behavior so here we're gonna go back to our raishin rest controller test and we're gonna say Marketo ma ki toh dot when this dot reservation pause did I find all then returned collections dot singleton singleton list new reservation so here it's gonna be one L and Jane right so we've got you know we're going to pre program it with an empty almost empty list or just with one value and I'm gonna run that test again alright so there you go that you can see that that works we actually got back there's response that we're expecting the content type is equal to or compatible with applications days and utf-8 and our application is now fully tested so if we go to the command line CD downloads beautiful testing may even clean package you can see that the tests are fast they're cleaning the beam and we should have now a working Springwood application now the concepts that we've looked at here are the foundations upon which concepts like consumer-driven contract testing rest and we've looked at that at least a little bit we look look to that in some at some length in an earlier installation of spring tips where we looked at spring cloud contract and had support for mocking out HTTP services there's more to discuss there we could talk about messaging and for example this also leads us very nicely into another discussion of j unit 5 so J naught 5 of course is a different programming model it's not backwards compatibility it's not it's not very backwards compatible that said it's easy enough to get to move code to it and it is a very very powerful paradigm it gives us a lot of benefits one of which of course is that I don't have to use on a wired private fields like this I can actually have constructor injection in my tests there's a number of other things that are very compelling about it and for the most part the spring testing apparatus works just as well we don't use they run with annotation anymore that becomes an extension in Jane and five Jupiter world but it's something worth looking into so the perhaps that'll be the topic of another spring tips insulation at some point in the future alright so with that my friends thanks so much for watching and we'll see you next time you
Info
Channel: SpringDeveloper
Views: 20,441
Rating: 4.9540229 out of 5
Keywords: Web Development (Interest), spring, pivotal, Web Application (Industry) Web Application Framework (Software Genre), Java (Programming Language), Spring Framework, Software Developer (Project Role), Java (Software), Weblogic, IBM WebSphere Application Server (Software), IBM WebSphere (Software), WildFly (Software), JBoss (Venture Funded Company), cloud foundry, spring boot, spring cloud, testing, tdd, agile
Id: lTSJCr7xdbM
Channel Id: undefined
Length: 38min 15sec (2295 seconds)
Published: Mon Nov 20 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.