 Welcome to the take control of your integration testing with test container session by Nareesha. Well, let's start with the take control of your integration testing with the test containers. Let's see if you demo how test containers will change the way you do your integration testing. My name is Nareesha and I help teams to get better with their technical practices. Since this is a short 20 minutes session, I'll be focusing on two use cases, how I ended up using test containers. To start with, I want you to think about, how did it feel when you were doing integration testing? Was it good? Not so good? Mostly, integration testing is highly challenging and not many people enjoy that, especially because you are not in control. You are in control of the system with which you are integrating. That is the main challenge of integration testing, making it highly difficult and people don't maintain their test well and ultimately, your integration test had fewer value as the time passes. Let me talk about the story one, where I already had an application in production, which was actually Grail's application. We had used Grail's framework for the application, where in which we had a slight problem, which was solved by introducing test containers. To start the story, the problem was something like this. Look at sample code here, where in which I have a list of cities, which is a list of string, and I'm trying to get all the conferences, which happen in the given cities. Essentially, this GARM code would get translated into this SQL statement. Essentially, it's where in class. What happens here is until then, we were using H2 for, which is an in-memory database, and we were using that for writing our integration testing so that tests are very fluent, and we already had good developer experience support from the Grail framework, such as we want to do some data setup right in the beginning, and at the end of the test, the database will be rolled back. So it was pretty easy to set up data and run integration tests, but there was one issue. So for example, this query would run pretty much fine with the H2. However, when we moved to, say, staging our production, we were using MySQL, wherein empty in class wasn't allowed by MySQL. So that's the kind of challenge we had. So to start with how it works with the H2, here I have the settings, which we were using, you could see the H2 driver, I'm using and it's all in-memory database what I'm using. So if I run, here is the sample test, the test is written in Spark, which absolutely runs fine and you would, the test would pass. So we assume the code would work. However, let's say we want to use MySQL. So in this case, I simulate using MySQL, I'm already running MySQL on localhost 3306, the database name is test app. See what happens in this case. The same test and we're trying to run the test and we would end up getting an error message. If you look at the error message, it's saying that it's an incorrect statement because it's not allowing empty values in clause, that's the issue. So if we had used MySQL for integration testing, we could have caught these errors upfront. So we did not, we were not aware of, or at least there were no test containers when we started the project, but then when we had one, we started, we thought of introducing that. So here is the additional dependencies, which is in Gradle build system. There's a bomb available for that. You don't have to mention the version for each and every tool. And there are integration for test framework. There is one for JUnit 4, there is one for the JUnit Jupyter and there's one for Spark assets. So I'm using a Spark test framework. I'm using the dependency related to that. And there is a direct support for several relational databases. Most of the popular ones are supported. And in this case, I'm using MySQL. So this is the configuration we need to add to write the code. And there is a convenient configuration available so that the driver class is, you can use a container database driver so that you don't have to give the very specific ones like MySQL or Postgres, things like that, which can be inferred from the URL mentioned here, which starts with the TC indicated service container. So following that, the database type is mentioned using that test containers will identify the type of database which you are using. A few more benefits I'll mention to you after the demo. So with this, there's a slight additional one statement we have to introduce. So MySQL container, we have to create an instance of that passing the configuration values. So I could use the test app as a database name and other values. Now let's sort try to run the, you know, our test and see what happens. So previously I had to set up MySQL instance on my own. However, in this case, I just need to start or run my application test. And, you know, this container will automatically use pick up Docker container, you know, with the MySQL image and it will run that and make it available for the application. Now we are getting the same error so that, you know, you can get the, which will reduce the feedback cycle and makes it, you know, much easier to identify the errors upfront because you are using the same setup what you would end up using in the higher environment. It's not an in many database. A quick recap of what the configuration here. Well, the important to note that, you know, typically you would have end up using the configuration twice once to create the container and once for the configurations, which you don't have to do that, which is kind of an improvement introduced by the test containers project after a while, like sometime in last year, I believe. So that you don't have to violate the dry principle. You could put all the configuration in one place. Since I'm instantiating the container only in one place, I have put it that. If you have multiple test classes, you could put these values in a configuration file. In that case, how you read the configuration will be, you know, dependent on the framework you're using. In this case, I have used a very framework agnostic way. My framework, I mean the application framework, not the test framework. Of course, it is coupled to the test framework, what we are using, but mostly it would be very similar with the slide changer if you are using the JUnit. With that, let's move on to story two. So an important aspect here is like, we were already aware of, you know, test containers availability of that. We had already used it in, you know, use case one. So this is like in another project where we were using, we were doing S3 integration with the S3 API. So we thought it could be a good idea to use local stack, which can be run on container and use that for your local development without using the AWS API for development purposes. Of course, on production, we would use AWS S3 implementation. So what happened is initially I thought I could start with the spike because that was the first time I was using local stack. It was not the first time I was using S3 API though. So I thought of doing it manually and I think I would have ended up spending at least 30 plus minutes. And there were several issues like the way URL would start with like a bucket name, dot host name, et cetera. So which was not very convenient, not a pleasant experience. So I thought of test driving the code by introducing the local stack dependency. So what you need to do is you have to add the local stack dependency here. Then what we would do is we would go to the code, something like this. Here is again the specification part. And I would instantiate local stack container. And of course, local stack supports in addition to S3, a lot of other services like SQS, et cetera. In this case, I just need S3. And I'm using Amazon S3 services, the kind of plugin I'm using here to which I have to supply the, which has to connect to local stack. That's why I take the configuration from the local stack and supply it to the service. Then I have a test to check, create a bucket and check if the bucket exists and I take a file, put it into S3, in this case local stack and then again try to read that, make sure the file exists and read the file and compare the file that it is what exactly originally I put, which is a sample one I had put, which is very similar to what we had used. So if I run this code, which would again spin up the local stack and your code will talk to the S3 implementation of local stack and which would work. So this is another convenient way of using test containers. Similarly, there are several other RDBMS which already talked about local stack is that there are several other integrations with which like Selenium API for example, which is out of the box available with the named API in test container. If you don't have a specific integration readily available, you could still instantiate something called generic container and you could supply the image name and test containers would download the image and run that and there are other integration support like if you want to wait for some service to come up on a particular port or things like that. So that the test container knows when this can be run because service has to be ready before running your test. So that can also be done by test containers. One important thing what we need to understand or why this makes easy, right? You could also say that, say, instead of using test containers, why not I just use Docker container directly? But in that case, you will have to spin up your Docker instance before starting your integration test and once you start your running your test, you don't have any control on the lifecycle of the system you're integrating with. However, in this case, what happens is, whatever system you're integrating, the lifecycle of that comes right into your test lifecycle so that you can control like when you want to start it or when you want to recycle that, et cetera, whether you want to use a single instance for all your tests or maybe you want to say, spin up the container, run certain tests, which can be done by... So you get that control within your test lifecycle. That's the exact benefit you get and everything can be done right within your code rather than doing it outside your test code. That's the kind of control you get. With that, yeah. So the code base is available in the given GitHub repository and for the documentation of test containers tool, you could look at testcontainers.arc. That was a quick demo of two use cases, what I had used. I'll be open for questions now. This question is from Narain. And the question is, can I have customized test container built on the go? See, what you need is a Docker image has to be available. As long as you can create a Docker image, you can create a generic container mentioning the image name and you should be able to spin off that with the test container. That's all the prerequisite what you need.