 All right. Good afternoon, everyone. So you'll already have probably went through these in the last two sessions, but it's mandatory. So please read the fire exit announcements. So for today's session, we'll be talking about test-driven development for microservices. My name is Reshmi Krishna, and I'm a senior platform architect with Pivotal. Hello. My name's Aditya Saikali, and I'm an advisory platform architect with Pivotal. And I want to set a little bit of context about what we're talking about today. What we're concerned with is, how do we actually test microservices with this thing called consumer-driven contracts? Our goal isn't to give you an encyclopedic treatment of it, to give you a sense of what the workflow is when you work with this type of technology. And this is all because, unfortunately, as much as we all love microservices, the challenge with microservices is that they talk to each other. So when things talk to each other, that causes problems. And there's two categories of problems that causes. One is around testing and service evolution, and one is around continuous delivery. So we've broken up the talk into a section that focuses on service evolution first, and how to solve that, and a second part with another demo that talks about how to handle continuous delivery. So the first thing is, we have all these services. And let's say I was in a situation where I have this green microservice that calls the rectangle microservice, which calls the Pentagon microservice. And what would happen if I was to add or remove something? How many people here raised your hand if you modified an API and were quite concerned that one of your clients would break? And you left something in there just because you didn't want anybody screaming at you for breaking the code? So this is the service evolution problem. Can I add something? Can I remove something? And if I did, have I done it in a safe way? We don't want to be with microservices going back to the old way of having to coordinate releases with a whole bunch of people. We just want to release each microservice independently. So there are a lot of patterns for how we can evolve our service in order to make sure that it does not break the clients. Things like, for example, how many people have versioned their APIs? You have version one of the API, version two. That's a common pattern, or maybe you build extension points into your APIs so that you can actually extend things in the future. These are all good patterns, but the one we want to focus on is something called consumer-driven contracts. Just a quick show of hands. How many of you are familiar with the consumer-driven contracts pattern? Just a few people? OK. So for those of you who aren't familiar with it, let's kind of get a couple of pieces of terminology down. The first one is, what is a provider contract? If I'm the person who builds the service, I can basically, from my point of view, all of my clients want everything I offer. So I'm looking at this and saying, OK, my client A is going to call me, and they're going to invoke every operation I have, and I'm going to return to them everything that they care about, everything that I can offer. But this isn't how the world looks like from the point of view of the consumer. So we look at it from the point of view of the consumer. Maybe client A, when it calls the service, does not use every feature of my microservice. It might be using a subset. Maybe I return 50 fields, and it only cares about three of those 50 fields. Has anybody ever called an API and didn't use every feature of the API they're called? They called, raise your hand? We've all done that. So what we want to be doing is understanding that the world from the point of view of the consumer is always very much a subset of what the provider offers. Every consumer is going to be wanting something potentially slightly different. It's possible that some consumer will use every single feature of the provider. That's OK. That's allowed. But let's make the assumption that they care about different things. And so what we want to define is this concept of, what is a consumer contract test? Imagine that if your consumers of your API gave you an executable test, if you're writing your code in Java, think they gave me a JUnit test. And I can run this test as part of my CD pipeline, and every time I make a change to the service that I've built, I can run my consumers' tests. And if I break any of them, I would know right away that I broke my consumer. But what the expectation is that my consumer is only going to test the things that they use. So if I call one of five operations that are available, I'll test that operation I call. If I care about four of the 20 fields that get returned, I'll check for the four of those 20 fields. So I'm testing things from my point of view as an end user of the API. And that allows us to have a very interesting flow of collaboration, which is referred to as the consumer driven workflow. So I've got a short animation to explain this. So let's say I have my code. And I've got a Git repo for my microservice. In this Git repo, I'm going to have the code that implements my microservice. I'm going to have my provider tests, which are the tests that I write to test my own code, as I should be. And then I'm going to have another directory in my repo. Let's call this one the consumer tests. Oh, there's a typo on mine. So imagine this column right here, not saying code to implement the microservice, but saying this is where my consumer tests go. Sorry, that's a typo on the slide. So what I do is let's say I've got the green team. The green team wants to use this. So what the green team is going to do is they're going to create a test for the API. And they're going to test for my microservice just what they want to use. Then they're going to send me a pull request. And in that pull request, I'm going to take it. And I'm going to put the consumer a contract test inside of my Git repository. So that I can run it as part of my regular development flow. And then the orange team comes along. And they also want to use my API. But they care about something slightly different. They are not using all the fields. So this is represented with the only care about the Pentagon feature, not the triangle in the circle. And they will send me a pull request. And I will put that in my Git repo. So now in my repository, I actually have a collection of all of the consumer tests. Every one of my consumers has told me what they need from me, not by having a conversation and sending me an email, but by sending me a pull request with a test. Who thinks this would be useful in your life? And the contract is going to be as the person who provides the API, my goal in life is not to break my consumers, but I want them to own telling me in an executable format what they need from me. So if we are going to implement this pattern, we probably want to use something like a contract testing framework of some kind. And the job of the contract testing framework is to make it easy to write these contract tests and make it possible to implement the consumer driven contracts workflow. So in the Java world, you'll see technologies like Spring Cloud Contract. This is what we're going to demo today. And other technologies like PACT and other languages that are other consumer driven contract frameworks. So the key thing here is that we want to answer first is, how do I actually represent a contract? Now, I'm going to turn it over to Rashmin. She's going to walk you through a demo on how to do this. All right, thank you, Adi. All right, so for our first demo, we'll be looking at a customer profile service with consumers as loyalty point and recommendation service. And our problem that we're going to solve here is, how can we evolve my producer, which is my customer profile service, without breaking any of its consumer? All right, let's jump to the code. So let's pretend, can you guys see in the back? Everyone, good? All right, so let's pretend I have a contract that has been sent to me by my consumer, loyalty point service. It's sitting in my repo and I need to do a, I need to accept their pull request. So before I do that, let's look at what the contract is saying and if I agree with it. So the contract is saying here that, hey, producer, customer profile service, if I send you a request with username and password, Rashmin in one, two, three, the response that I'm expecting from you are these two fields, user login check status, end points, with gold and 50,000 as the value. So this is what the loyalty point service is expecting from me. I think this is OK. I can do this. So let me accept the pull request. Let's put it under the contracts folder. So as Adith was mentioning, in the producer, you have a common repository, which is my resources contract folder, where I, as a producer, can merge all the contracts that's coming from the consumer. It also makes documentation really easy. So this is in Groovy DSO format. You can write it in YAML. And you can also generate docs from it. Isn't that cool? All right, so now let's see once I've accepted this contract. Let me run my project and see what happens. Let's bring up my console. Looks like it broke. So let's see what happened here. So because I accepted this contract, what you see here is my consumer here is expecting a field called user logging check status. But what I have here is status, so that's not matching. So since I don't have any other contracts, I think that's a valuable change. And I can make that change. And of course, this is a demo. Let's make that change and then see how that works. So I'm just going to rename my field, also rename the getters and setters. OK, all right. Now let's run this again. And hopefully they should build this time. All right, build success. Great. So once the build has been successful, let me show you something. It has also generated a test for me. Isn't that cool? So the verifier plugin on the producer side that I have from the Spring Cloud contract project has not only able me to put my contracts in the repo and make sure I conform to it, but it has also generated, auto-generated a test for me. And this test makes sure, because of this test, my initial implementation broke because the field that I was using was incorrect. Does that make sense? Yeah? All right. So moving on. In summary, what you saw so far, I was on the producer side. And once I accepted the contract coming from the consumer, because I was using the verifier plugin, it was able to verify that my code is validating against the API that the consumers are expecting. And it was also able to generate these tests for me. Back to Avip. Yeah. Any questions so far? So we saw how we can solve the problem with the consumer-driven contract workflow of being able to change the definition of an API for a microservice and then determine whether who we broke and what action we need to take to fix that. The other problem that we have when we want to do microservices at scale is that if I'm going to make a change to the circle microservice and I want to deploy it, as part of my pipeline, how do I run an automated test against the circle microservice? Do I need to first deploy the triangle, the square, the pentagon, and the diamond? If I have to do that, then my pipelines are going to be really complicated. We don't want to, we might be able to test the circle microservice and know that it's working before we actually deploy it with all its dependencies. So this is the how do I test something in isolation? And so there are actually two strategies for doing this. One is to use mocks and the other one is to use stubs. And mocks and stubs are kind of slight variations of the same idea and I kind of want to take a moment to distinguish the two. So when you're building a mock, typical actions are first you create the mock and then as the person writing the test, you set the expectations on the mock. So you say, hey, when I call you and I pass in the username and div, you should come back with a response of 50,000 points status platinum. And then you use that in your test. The key idea here is that every single test first sets up the mock for their needs. And it tends to be the case that you don't make remote calls when mocks are involved. You're typically relying on the programming language and the runtime of the programming language to maybe do something in Java we typically do like byte code generation in order to generate mock implementations. You use tools like Moquito and other frameworks like that to auto-generate the mocks. Stubs, on the other hand, tend to be things that are the same for all tests. So in a stub, I'll make one stub and multiple tests will utilize that. So with a stub, we also tend to be able to do remote calls. So I might actually start up a dummy server. I might use something like WireMock or another technology service virtualization technology. And this way I can actually receive real messages, real HTTP requests, and return canned responses. Who's built a stub by hand in the past? Raise your hand if you've done that. So a few of you have. That's great. And so if we look at this scenario and we say, OK, I got my loyalty point service. It calls a recommendation service, which in turn calls customer profile, which calls order history, which uses a database and makes a call to a main fit. If I want to test the loyalty point service, I don't want to have to, how do I get an on-demand instance of the customer profile service and all its dependencies? So what we'd like to do is instead and say, OK, how about we do this? What if we actually hand coded a customer profile service stub, and then that customer profile service stub, we can deploy that to Cloud Foundry, or we can just run it as part of our test. But the problem with hand coded stubs is that they are tedious to code. It's actually hard to make sure that your stub is staying in sync with the actual API that you're calling, because your stub can veer off. And you're getting surprised that, hey, my tests work for the loyalty point service. What do you mean when I deployed it? It didn't work because the customer profile folks have changed their implementation. And I didn't know that, because I was testing against the hand coded stub that I wrote. And the stub does not test the request over that. Like the stub is going to give us, we want to test it over the network. So what we want to do is this. We want to basically take the contract that we have written, and then we want to generate from that contract a wire mock configuration, which implements what's specified in the contract. Then we can launch wire mock as a server, pick a port number, and make our request to wire mock remotely. And that also gives us the advantage of being able to publish this wire mock configuration into our Maven repository so that we can just depend on it. So what we'll do now is end up in this situation where if I want to write my loyalty point service integration test for my loyalty point service, I'm able to do that. And I'm calling the auto-generated customer profile service stub. And this is going to resolve the problem of keeping it in sync. Because the contracts are being used to generate the tests that the service provider is using to test their code. And the same contracts are being used to generate the stubs. And since the contracts are part of the pipeline of the service provider, I'm going to resolve that problem of using a version of the stub that doesn't actually reflect reality. So this is the combination of being able to generate both is the kind of really key idea here that enables that development workflow. So what should you expect from your contract testing framework? So your contract testing framework should be able to generate the stubs for you. So what we're going to do now is turn it back to Reshmi to show you how this works in Spring Cloud Contract. Thank you. All right. So before we go in to see how the consumer is going to utilize the stub, let's see something. And to review, what we saw so far in the demo was we have a provider, Customer Profile Service. We have an auto-generated test from our verifier plugin. And if you look at the jars that was developed in the target directory, you can see here is the project jar. And you see another thing, which is very interesting, which is the stubs jar. So when I ran my producer using the verifier plugin, what it did, what it created this wiremark configuration as stub.jar, which then was downloaded into my Maven repository. All right. So now let's go back to my, sorry about that, consumer. OK. So this is my consumer. This is one of those green boxes, loyalty point service. What it does is it tries to retrieve the points given the name and the password. It retrieves the point and the status from the Customer Profile Service. You see here I already have a test. Let's run this test and then see what happens. I'm going to run this. Great. So it was successful. Now let's see what happened here. OK. So what you see here is it's telling me I've started a stub server for this project on port 665. And the stub that I have here is the Customer Profile Service Stub, so it's the producer stub. So as a background, the stub runner plugin that I have for my consumer, it does two things. It goes and it downloads the stubs from the producer and makes sure it's available to me. It's running on port 6665 on wiremark server. Then using wiremark, you can also see here this is my request that I've generated. And it's telling me looks like your request is matching and the response that you're expecting is golden 50,000 is also matching. So that means the test that I've written matches with the contract and the stubs that has been generated from the producer. Now let's see what makes this happen on the consumer side. So as I was telling you before, we use stub runners. So if you see this annotation here, add auto configure stub runner. So the moment you have this annotation in your code, you're telling it that, hey, I want it to use my producer, which is my Customer Profile Service Stubs, it's going to run on 6665. And I want to make sure that my tests are conforming to these stubs. So now let's go back. Any questions so far? No? Everything is super clear? All right, great. OK, so what you saw on the consumer side was I wrote a test and I had the add auto configure stub runner as annotations on my test class, which allowed me to do two things. So as part of the stub runner plugin, from the contract, Spring Cloud Contract project, it downloaded the jars with the stubs and I used a class path to be able to verify that. And it also ran these on my Varmock server that then I was able to validate my test again. Right? Yeah, so just going back to taking this step back and saying we showed you a couple of different things that together make it possible to implement this advanced way of testing. And that's the idea that we're going to write the contract once. We're going to write the contract in Groovy. The reason why we want to write the contract in Groovy is having Groovy is very good for making DSLs and it allows us to create contracts that aren't static. They're not just like when you see the string return this result, we could have put a lot of fancy things in there called Java code to help generate the incoming requests and the outgoing responses. We don't have time to show all of that. But the point is we're gonna write the contract once in Groovy. We are going to run it through the SpringCloud contract plugins. Now SpringCloud contract has plugins for Maven. It also has plugins for Gradle. We're gonna expect SpringCloud contract to do two things for us. Number one, we wanted to generate JUnit tests which just run as regular part of our implementation of the microservice to automatically test that we are conforming to our consumers requirements. We are then going to also generate stubs. It's gonna generate stubs for us. We will publish those stub jar files to our Maven repository. So our colleagues that are working on calling our services are able to use right there integration tests just as plain old JUnit tests but just add the annotation at stubrunner to it. And by doing that, it will download from Maven the actual WireMock configuration, launch the WireMock configuration as part of the test. They're just gonna go on local host to the port number that they specified in the stubrunner. And now you have the ability to actually do the two things that we wanted to do which is be able to evolve your API's interface without breaking your consumers and to be able to test things in isolation. So if you think about it, is this going to be a complete replacement for the end-to-end tests? No, you still wanna do that. But what we wanna be able to do is on every commit, we wanna go through the testing pyramid. We wanna run our JUnit tests, our regular unit tests, we wanna run our integration tests. And if all of these pass, eventually we're gonna hit an environment where we are in fact doing an end-to-end test to validate that everything works. But let us be able to disqualify and catch errors earlier and have a shorter feedback loop because this is really what this is all about. Absolutely. And just to summarize the whole flow that you saw so far and we tried to keep it really, really simple. So what we saw so far as a developer, what I did was I generated a contract that then it was accepted by the producer. And what this contract did was it mentioned the interaction between the consumer and the API and we used GroovyDSL for that. You can use YAML, config files. And using the contract Maven or Gradle plugin, we then generated the stubs, which were then downloaded and used by your consumer. And the way your consumer used these stubs are using these at-auto-configure stub runner annotation, right? So what this did for me as a consumer, it kind of took away those environmental issues and waiting on the producer to publish it code. I could very well run and test my code against the producer without having to call the real API. Now I'm guessing you all have a CI CD server that's running, right? And you can very well automate all of these things. And Sprint Cloud contract actually starts the wire mob during the test execution and also configures it with the scenario that's specified in the contract for you. And then you can run your test from the consumer side against it. So any questions so far? We are ending, I know we have like less than five minutes. So any questions so far? Yes. Yes, it's available. And you'll see that in the resources slide. Yes. So the question is, is there any tooling that allows you to generate the contracts from your code? And not that I know of with this one. We, like the code that you're generating the contract from would have to be the consumer code. And that's a pretty hard problem to understand how one piece of code is using another one. I think a big value of the contract workflow, the consumer driven contract, it makes it more social. It's leveraging the idea of pull requests, coding as a social activity and it's asking the actual end user to say please take some responsibility and give me a test. It's a way of spreading the testing culture inside of the organization. And it's saying I will reward you in two ways by you taking the time to write the test. Number one is, I will give you, I'll make sure I don't break you as a provider. And number two, I'm gonna make it easier for you to get stops. Yep. Great question. Yeah. Yep. Save it to the contract. Yes, absolutely. And with that, I can see all over time that the contract would become over the contract. Yeah. Because it depends on, is there any solution to follow to modular contract in the way after we use contract saving this contract? Yeah, so let me summarize the question. So the question is an observation that the can, one, can I have dynamic contracts where I have conditional things? Number two is, what if the contracts get so complicated that nobody understands them anymore, right? So the answer to this is yes, because we're using Groovy, there's the concept that your base contract are actually have a base class that you can control and put helper methods in. And like all technologies, you can definitely make spaghetti code with this. So part of the learning process, so we encourage you to keep things simple and like say, hey, consumers, can you please just test what you need? It's easier to have 100 simple contracts and to have one generic contract that does 100 cases, right? Right. So that would be the recommendation there. Right. And another thing is, as you saw the endpoint, right? So you can customize the contract to make sure you're only kind of integrating all the test or all the use cases for one single API call in one contract, right? Just to make sure it's not as convoluted. All right. So before we take any more questions, actually let's finish up. And I know we have a minute. So did you wanna talk about the next features? So I'm gonna say Spring Cloud Contract has a very large project with a ton of features. Like we would have loved to have like five hours to tell you all about it, but we didn't. So we hope that what we've given you today is just a glimpse of what workflow it enables and the motivation to look deeper into it. A lot of the more advanced scenarios we can do it. So just we encourage you to go to the docs. And this is a list of resources that you can head to find more information. So question is in the back? Yes, yeah. Yeah, so I'll... I've seen the video best case actually. So this is a wonderful question. So I wanna repeat it for the video, which is can I write the stubs? Can I get the stubs before the implementer of the microservice has added that feature? And the answer is absolutely yes. So if you think about the idea of TDD, you're supposed to write the test before you write the code, right? And we tend to do that at the level of an individual class or individual small piece of code. Consumer driven contracts actually allow you to do that at the level of your architecture. Some you can describe consumer driven contracts as TDD at the architecture level. So if I'm a consumer and I know that the other team I'm calling the microservice that I'm calling, they haven't had time to implement the feature that I want, but we've had a meeting, we've talked about it. I can specify the contract, get that in and start like writing my own application before they've implemented that feature. And like there is a way, for example, to store all the contracts in a separate repository. There's a lot of variations of this workflow that are refinements, which we didn't have time to talk about, but wonderful question. Thank you. Yeah. Any other questions? I think we're done. We're getting the signal for, we're finished. All right, happy to take more questions offline.