Better API Testing with Consumer-Driven Contracts Using Pact

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
i'm going to kick off today's discussion and answer any questions you all may have in the chat and my colleague and community director of ultimetric collider jacob smith will also be here hanging out with us my name is ryan robinson jacob and i work for a digital transformation company called ultimetric who help our clients stay ahead of the curve and they help everybody they stay ahead of everybody in tech and innovation today we are excited to showcase one of our very own ultimetric engineers jawad l turk who will be exploring how consumer-driven contracts make it way more efficient to make changes to apis and i can't wait to actually grasp what this concept is even about so i'm going to leave it to our expert jawad to help us out first of all thank you ryan for the introduction i really appreciate it and i hope this will be a fruitful event today so for those of you who don't know me my name is jawad alturk i'm a software engineer currently working on the av task project at ford um i'm a back-end software engineer so i currently work with technologies like aws and currently we're moving to gcp uh if you're part of ford i'm not sure if your team will be adopting this new technology anytime soon but what i'll be talking about today is a part of spring boot so if you're working with spring boot this is going to be very useful to you especially if you work on apis so if you deliver apis or you consume apis so our topic today is consumer driven development this helps cross-functional teams agree on like a contract between them to generate an api now i'm pretty sure uh most of you here have either consumed an api before or wrote an api and sometimes it's really difficult to um have an agreement between teams on what the api should look like what data you will be returning and things like headers and other stuff so this is basically a solution for all of us and it's called pact so what is packed packed is code first tool for testing http and message integrations using contract tests which means um the consumer like whoever is consuming the api and the provider will agree on the form of the api and this will be written in a contract of course using a code contract tests asserts that enter application messages confirm to a shared understanding that is documented in a contract like i uh mentioned and without contract testing the only way to ensure that applications will work correctly together is by using expensive and brittle sorry about this integration tests um so like postman pretty sure some of you wrote postman test for integration so this will make you get rid of all these postman tests which are like third party software this will actually let you write code in java similar to junit so what we will be working on today is very similar to junit things you're already familiar with and if you're a spring developer you should also be familiar with the annotations i will be using today we will be using junit 5 so those of you who haven't worked on junit 5 yet it's not a big deal most of the code i used is very similar to junit 4. the only difference between junit 5 and 4 when it comes to fact is the annotations so you can take a look at the documentation i will provide a link by the end of this session and feel free to test with it and explore next slide please ryan okay okay so this is the next slide so what is contract testing contract testing is a technique for testing an integration point uh by checking each application in isolation to ensure that messages it sends or receive confirm to a shared understanding that is documented in the contract so like i said before um here it's called consumer driven development for a reason which means the consumer of the api will be the one responsible for generating the contract and the pro provider will basically run their controller in this case because we're working with spring boot today to verify that the result they're returning is in line with whatever the contract is saying for applications that communicates via hp uh these messages will be the http request and response and for application that use cues this would be the message that goes on the queue so pact is very versatile um consumer driven development technique so you don't really only have it doesn't only work with the spring it can work with i personally use it with aws it can work with cues like the slide mentions so in practice a common way of implementing contract tests and the way pac does it is to check that all the calls to your test doubles return the same result as a call to the real application would next slide please so i haven't really tested with azure um but pac supports different programming languages i know azure is on um i believe net so i'm going to leave this to you because as i said i'll be leaving the documentation but i believe it does support it does support this and we can discuss about this later okay so contract testing it sorry is immediately applicable anywhere where you have uh two services that needs to communicate such as an api client and a web front then like i mentioned before although a single client and a single service is a common use case contract testing really shines in an environment with many services as is common for microservice architecture having well-formed contract tests make it easy for developers to avoid version health contract testing is the killer app for microservice development and deployment can you move to the next slide please all right okay so terminology is here in general a contract is between a consumer for example a client that wants to receive some data and a provider for example an api on a server that provides the data to the client needs and here it can be any two applications it can be a backhand and front end today we will be focusing on just spring boot again so you will see both the consumer and provider written in java and springboot in microservice architecture the traditional terms client and server are not always appropriate for example when communication is achieved through message queues for this reason we stick to consumer and provider in this documentation and i believe this is the last slide for this presentation i just wanted to define what is packed in general and who is the consumer and who is the provider so now we can move to the hands-on demo okay so first of all sorry about that closeness you don't need it uh so this is first of all the packed um website can you guys all uh see my screen looks good okay so this is the packed website um as i said before it does support multiple uh programming languages i personally only uh experience i'm only experienced with the jvm or spring boot um but it does support.net and other uh famous programming languages like python c plus plus and others um so maybe during the time i'm um explaining this you can go on the website and see if or just google search it if they support it um but let's move to our presentation now so uh i believe you guys are already familiar with spring boot i'm gonna be uploading this uh project by the end of this session um to github and i will send the link to ryan and she can forward it uh to everyone who is attending this uh session in case you guys want to uh test with it uh so first of all um to begin with we said there is a consumer and a provider consumer basically is the one who is responsible for generating the contract as i said before and uh the contract will basically have things like uh the method uh of your api like whether it's a post get um update delete uh also your api might have headers uh it might have a body it might have a query parameter and many other stuff uh if you're a backend engineer you should be familiar with these things already so to begin with i'm using hero j unit five so first thing you want to do is just writing a new client pack test so i can start this so i'm going to start by client or we can name this okay so first thing we're gonna do is we're gonna start by extending our class with pact a unit consumer okay so that's the first thing this will basically tell pact to run your test uh using their extension which will basically generate your contract and will generate um sorry to just generate your contract and also run the test you can you as a consumer you can also verify that your contract is built correctly and we can go over this later another thing i want to add is basically so what i'm what i'm up what i'm about to type now which is the packed folder this defines basically where you want your contract to be stored by default it's either i'm going to go back to this slide because i added some uh notes here so by defaults okay i think i erase them by default it's either under under acts so here i define it actually packs so i changed that directory uh it's either let me go back to it okay either under target packs or build packs in case you are using gradle so here i wanna actually uh put it in a folder called packed in my main directory so i did that so you can see here it generated packs and i'm going to run this later i'm going to stay in this class just because it has the notes and i'm going to be what you basically want to focus on is how to generate your contract and then this is the test which is annotated with tests to actually verify that your contract has what you are expecting so one more thing here you can set your provider a provider can either go on the class level or on the packed level so here you can set any other provider you want since i only have one provider here uh i i set it on the pack level uh sorry at the class level um and it should be uh good for generating all the facts uh within this class uh here you can also so that's that's basically it for the annotation you will add to your class when you start if you go to the documentation of pact on their website you might find things written in a different way there's multiple ways to implement pact you can either extend a class or do it the way i do it i find this way easier for me because i have i can control things better this way so next here we are gonna just generate our pact so generating a pack starts by annotating our method with add pact and adding the consumer name so here i my provider will be ultimatric let's say you have a team that provides the ap sorry that consumes provides the api is ultimetric and the team that consumes the api is ford so it's going to be ultimetric up here and port down here um so what does ford need from ultimetric they will come and say so keep keep in mind this name here it is important because you're gonna be using it in your pack test for so this is basically referencing the method you want to call from your contract after you generate it to fetch the data from the contract and then verify against it so one thing you you need here is uh the builder or the pac dsl with provider this is basically responsible for uh generating your contract first of all let me show you what the contract will look like after we generate it and this happens just by running the test so this is how it looks like basically it's a json format you have the provider name you have the consumer and you have all the interactions interaction includes the request and the response and in case there is any rules that you set let's say you want a vin that look uh that takes a regular expression so you can also define this in your test uh and yeah so it's pretty straightforward so going back to my test here um first thing you want to take care of is basically the state why is the state important because this think of this basically as an endpoint that you have in your controller um so one endpoint will basically return uh an object a json object uh with data right so uh in case everything you receive is good like let's say your headers are there if you have a body that is correct is there so you're going to return a response entity with a success 200 status code so this is how i define this state of my controller or endpoint so i say let's say and you have an endpoint called add user or fetch users or whatever and in this case it's returning success response so i add this and uh here it's just giving details for your um packed response and here you define the path so the path path basically is what your controller also responds to when you hit the api um so you guys are pretty familiar with this here the method you define the method which is either a post or get or update as i said before and if your endpoint uh requires headers you can define these headers i just basically wrote a hash map up here and i defined my contact type content type which is application.json after this if you have a body in case of a post most of the times you will have a body um so here it's a string format so i i just used json to map my um dummy payload here into a string format so this is pretty much it for defining uh the end point you want of course there's many things that comes with it what else we can find query for example like i said before if you have a query um yeah and this is pretty much it so after this so here this will define your endpoint and this will define what is the expected result you want from this end point so if you have an endpoint that lists users for example you're expecting the response so here after you define your endpoint you will say what do you what do you want the response to be like so you will say it will respond with you want it to respond with status code 200 and you want the body to have something similar to this so here i'm actually defining uh these hard-coded values because i'm gonna just show you my controller for a second here um so i have uh send user off here for example i actually forgot to rename this uh it should be get user um or add user here so i'm asking it to return a response entity success and my response entity success looks like this it has a response object which has status code 200 a message success and it has the hdb uh status okay which is it's 200 here so yeah so this this endpoint here in my controller it's just returning a success so pretty simple so that's what this test will look like uh so i'm going through this just now you guys have a general idea of what we are going to do and then we will work on generating a pact and a provider test to verify against the contract so that's it basically then after so the body can have many forms you can either define these this way you can have uh if you let's say you have a list of object you can put minimum array like here you will define the name of the array so let's say we have data the size of it and so the array will have an object that might have see here string type so you will have a name for example for your object you might have another string type was with last name here in your list then you can close your object and then close your array but we don't need these so i'm going to remove them for now because this test is working we can verify this later since we close the array it's different now so that's basically it for generating the contract and you will see the things reflecting and this i'm going to delete these contracts you should see them and when we add something you should also see it here so one thing i want to talk about here is this property here in your gradle uh this will basically allow you to overwrite whatever is in your contract so let's say you come and your team needs a new endpoint so you want to update your contract this will basically allow you to override what whatever is in this contract and instead of just appending to it because if you have like certain endpoints that are changing uh you don't want the old code in here so this gives you the permission to overwrite whatever is in um your contract so going back i can just delete this okay so the next thing here uh this is basically test only a test uh to do what to verify if uh whatever is in my contract is actually working as i'm expecting so what i have here this is basically a mock server the mock server can be injected directly into my test method it comes as part of pact consumer another way to test this is basically using a mock server or sorry mock provider rule uh this is a part of junit 4. since we are using junit 5 they provide us with this way or method to fetch my end point and here i'm actually using unit rest i'm not sure if you guys are familiar with this but some of you might be here you don't really have to use unires you can use mock provider rule or any http client basically to fetch this this is probably the easiest way to do it and as you can see guys now this is as if i'm making an actual api call so one thing i actually forgot to mention here in addition to uh the provider name you can define other stuff like the host name sorry port and host name so here you can define port you can define host name i'm not sure if this is part of junior five but you can def definitely define your host name and the method in case you want to test something other than your localhost uh but you are limited to a few things let me show you what you are limited for okay so you can you can define your host name it can either be localhost or one two seven or zero zero or host name and so these are the only valid uh host names that you can use so going back to our test now so i'm gonna just leave the default whatever is default i'm not gonna change it this is why um you're not seeing me here setting a host name so i'm gonna debug this and run it first of all i'm gonna actually delete this and i was actually starting to test with this but let me remove it so here you can see we have one packed and we have one test to verify uh this pact i generated uh something you need to pay attention for is the pact method this is uh part of pac test for nj unit five you don't really have to define it in case you're going to be working with junit 4 you might see something called packet fragment i prefer to keep it like this because i think it's neat and it keeps things separated so i know what what pack i'm actually testing for uh so if the names here if you decide to go for packed method and uh your names are not the same this will probably throw an exception and uh let's do this and i'll show you so i'll run my test here so as you can see i deleted my um i can actually delete this folder as well and it should generate it for me this will test now okay but of course it failed because it cannot find the directory so i'm gonna set this back to what it was let's do something i'm gonna do a clean build i was failing too let me see here about my test again still failing sometimes it behaves i'm gonna remove everything here i was testing before i started this i'm sorry guys so run again okay so here we can see actually that generated the old one i don't want this because mike is so the naming convention that will be so it's acting up right now hey jawad are you able to uh we have a question says are you using intellij id oh yes and yes yes i'm sorry i cannot really see the because i'm in full screen mode though oh no it's okay i'll help you but yeah so yes i am using intellij um this is intellij it should be work with any ide basically if you're using eclipse um this should work also with maven and gradle i personally use gradle so this should work sometimes it acts up so i'm gonna go back to one of my this should be the same jesus okay okay let me so my last thing to do validating and restarting this does this sometimes i'm sorry guys so as you can see we didn't change anything it was just the cached or something so i'm gonna put a debug point here to show you what we are getting back from the uh request body here um so here if you are testing with the request body of course it's gonna be request body if it's just a get you will use http request only so we can actually do a test with the with a get endpoint but for the purpose of this demo i actually wanted to make sure i have at least one end point that is currently working um [Music] so let's let's continue with our demo okay so i'm gonna debug this and we will see what this is actually returning so as i said before um this will basically come and uh read this according to the end point you have um and it's a mock server and it will so it will know uh which event or pack you are testing for from the so if we go back now to our pack it's generated again this is the path i have this is the description and what i care about is the state so you can see here uh that's the that's what you that's how packed will fetch this for you according to the state uh sorry this will and this is in the provider case um in the case of the client it'll be according to the to the name here uh so let's see the debug so this is my object basically if we go back to the contract you are telling it the body return will say something like success and status code 200 and you can see here this is what your json object looks like it has two fields message and success and then the rest is just junit i'm asserting that my status is 200 and my return message is actually success now that that that's basically generating the contract now that's on the consumer side the next step would be for the provider so let's say you are the one who's writing the api to actually serve the consumer so in this case you will be using something called packed provider and here you can see the dependencies i already added them for you guys this is the consumer in case you want to use it this is the latest version and this is the provider dependency i'm gonna go back to my pack now here as you can see uh since this is spring i used mock mvc some of you um should be familiar with this in case you use spring to test your end points so that's uh the same i again defined here my folder where i will be reading my pact from so it'll come and check this the provider as well who's who's the provider for me for this class now things you need to add here as you can see i have a test that is completely empty the reason i have this empty is because the in the case of provider this will actually go without even running the class take my instance here i define my i don't think define it in this case there is a other method where you actually define so i will show you this method so this is the consumer when you go to jvm this is a consumer the provider this is the provider for junit there is multiple ways to actually write a contract verification test uh i personally am using let me see here so here you can define actually your controller if you want but i just simply use mock mvc it makes your life easier and the this the test to verify a certain state is from the actual given name that we added before so going back here you will add these your test template and this is basically extending the pack invocation context provider uh which will basically allow us to verify against the actual endpoint so here you guys uh in the case of provider this will actually run and call my controller uh here i did not define which controller it needs because what i'm using here we'll just use mock mvc and get the add user endpoints with which already returned success and http 200 so the provider will run against your actual controller and by the way consumer and provider does not have to be in the same project there is multiple ways to generate a contract you can either publish your contract to a packed website there's using something called packet broker where you will add the link to your uh packed account in addition to an authentication token and you can publish this to the website uh or in our case this is the free way of doing it and just by generating a uh a contract now there is a gradle uh task that can basically publish your contract so let's say uh you are the provider but you have your own you have your own project so what you can do is um the consumer can basically write a task that will publish this to your github repo every time they run it uh so for here i don't have it now because i'm in the same project so i just needed to to define the pact folder um and of course if you change the directory here uh you need to define which packed folder you're reading from you can either define it here on or in the gradle again so now here i'm gonna just run this uh all i needed is the state uh and which state and i don't have to add anything to my test there's multiple ways to writing a verification test in aws i can show you for example my own project so so here you can see i have multiple packs generated according to the providers and consumers i have the same consumer but multiple providers so in my pack test i have the consumer test for every endpoint i have for the verification i'm using aws so instead of using something like mock server in case of um spring boot i can still here use mock server but it's a different instance i just named it mock server let me check which one so here you see this in case of aws i i use something called client and server to create a mock server uh but in in spring boot it's a lot easier it actually comes with a mock mvc so you don't have to do anything so now i'm gonna just run and show you the result in the log who would like to see me writing a uh a pact again maybe with a different uh endpoint name um or i actually have a in my demo controller so actually let's make our provider fail by introducing a uh new pact to the client would you guys like to say that uh to see this we still have seven minutes i believe are we short on time jacob or we're good we are good but yes we are scheduled until 5 30 so but we can hang around 6 30 or yes 6 30. sorry yeah so let's let's actually let's actually do this so i'm gonna copy this and write a new uh so i already have an endpoint here um in my demo that returns a list of users um so maybe we can test this uh so first of all this will be this actually add users so i'm gonna rename it to list user success and here will be list users user here this has to be uh similar to what you are expecting your provider to provide you with so um the endpoint name since i already have it i'm just gonna name it the same way it's users and in this case it's a get so i'm expecting it to get um get mapping here by the way you know as a consumer you will not have the controller so i'm just looking just to have an idea of what i'm expecting from my provider to give me so i'm going to name it get it has headers yes does it take a body no because it's a get and what it will respond with is so if we go back here my response will be response object will have 200 value success and list so i will do the same thing here have a message success and it will have dot minimum array like so uh there is multiple ways to uh writing a raise uh minimum array like is basically uh guaranteeing that at least one item and an array you're looking for is there for you there is other stuff so in case you want to see what the body can look like you can search pack the esl json body this is also part of the documentation here they will show you how you can generate so if you let's say you have a string with a value you have a number with a value only string type this will match any string so if you let's say you just want a name um so you can define this so here if we scroll down this will ensure that each item in the list matches the provider provided example and the list is no smaller than the provided name provided minimum so actually i think i use the i have the wrong version this is part of v4 i think i have v3 this is what i think the right way to use this is use unordered array it'll just ensure that the list matches provided example ignoring the order but this here will if you have more than one item in the array it will test against all the items to ensure that they have what you expect but anyway uh i think i'm gonna just write one so minimum array like now here it's not a big deal so going back to my controller uh my if i go to my response object this is also something i wrote is just a simple java object and the expected field name for the list is data so i'm going to use data here and here you can set the minimum to one and here you can put object and dot name or string value string type so uh here i am my list has basically this is a dummy payload it has just a first name and last name so i'm expecting my list to have two fields one called first name and the other one is called last name last name and then you close your object and you close the array not sure why this is let me see so um okay so this will basically okay so now we can run the test so i'm going to take this again here now what we are testing for is packed for list user success so i'm going to copy this and add it to my pack method name and here for now this says list users again the end point that i'm calling now is different this uh according to this is just users users and here it's not with body anymore it's just http request peer request so and it's a get so we're gonna get this uh do i have headers yes i do have headers and i don't need a body now this is same thing okay so a start equal here as well so i i also i define here i just expecting one item at least right but you can define as many as you want and you can basically um so the reason i i do this um you can basically get rid of all of this by the way and just use like to json.json um like dummy payload and this should be enough but this will be like this will not be useful to you because um if your api is dynamic so if your list values is changing this will basically only work in case your body is always returning the same value so let's say you have an end point that returns the same thing every way you can use this way but if not then you would have to use the pack json body like i'm using here uh okay so i'm going to assert the size now of my my object here get its json array oh theta dot i get size here not sure if i can get size on this one the length okay so i'm expecting here since i told my test that i want just one uh so when when this will work um it'll generate only one item with with like random values in my contract and hopefully this will run as it is so we can see what the contract will have okay so it fails on failed on this let's see what it says and okay supported see oh i don't need the object so in some uh cases like if we go down here you will need that object array containing okay as you can see my test passed if i go back to ford ultimetric now uh in the past you saw there was um [Music] only one interaction now we should have two here let me see why i'm not seeing it so that's at user should see a list user let me see maybe that's okay okay now uh i'm sorry like setting it to true um did not generate it for me but anyway so as you saw before there was one state which is add user and it was only for add user now you can see your pack contract has a get method here and the users and here you can see the data it just gave it a random value so if you want to give a defined value so let's say you are set returning a message success in this case you can do like what i did over here to generate it by using string value instead and as i said you can refer to the uh to the documentation that i just showed you um so that's pretty much it uh we're like five minutes over i'm not gonna write the provider test for this but i believe you have a better understanding i hope this was useful for uh for you guys so uh feel free to ask any questions if you have some again i'll be uploading this to github in case you guys want to play with code and write your own other than that thank you guys for attending thank you ryan and jacob for hosting this event i really appreciate it um feel free to reach out guys if you have any questions i would love to help you um thank you so much have a good rest of your day thank you good night everyone goodbye thanks everybody have a good night
Info
Channel: Collider
Views: 1,073
Rating: 5 out of 5
Keywords:
Id: RAOg69WLUAQ
Channel Id: undefined
Length: 51min 34sec (3094 seconds)
Published: Mon Mar 29 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.