Test-Driven Development (TDD) is a discipline that helps to create error-free, quality code, and drives productivity by reducing and eliminating errors as well as providing many other benefits.
In these video tutorials, you will learn about TDD, its key principles and practices and introduces test-driven development with JUnit5.
Click play to begin your journey through the tutorials.
Test-Driven Development [4m 47s]
Unit Testing [7m 11s]
JUnit5 Introduction [7m 12s]
Java Bitcoin Library Design Review and Tooling Setup [4m 48s]
Initial Project Setup Using Maven and First Set of JUnit5 Unit Tests [24m 3s]
Welcome back. In this first demonstration, I'm going to clone this directory here, locally. And then show you how you can use the Vagrantfile within it. To stand up your own development environment, which when launched, we'll go ahead and install Maven three and the open JDK11 for us.
Let's begin. I'll copy the repose, get clone URL. And then I'll jump into my terminal, and perform the clone. I'll now navigate into the new Java dash TDD dash but going converter directory. I'll do a directory listing. And again, here we can see our Vagrantfile.
So if I clear the terminal, and then I print out the contents of our Vagrantfile. We can see a few things. Firstly, it's going to install open JDK11 for our Java development, followed by setting up Maven 3.63, which will give us our Java project management capabilities. And all of this is done on an Ubuntu 18.04 version.
So let's go ahead and use the Vagrant up command to launch our development environment. Excellent, our Vagrant instances now up and running. And even better, open JDK11 has been pre-installed as well as Maven three. So at this stage, I'm going to jump at the Visual Studio Code, running the following command, within the current directory.
From here, I'll start the integrated terminal, like so. I can then start a session on our new Vagrant instance by running Vagrant SSH within the integrated terminal. That's great. So we've now got an SSH session on our Vagrant box.
So the next thing I wanna draw your attention to, is the following fit, and that is, Vagrant mounts, the slash Vagrant directory, back to the host systems directory that contains the Vagrantfile. Now, this is a really cool thing because what it allows us to do, is use Visual Studio Code to edit the files on the host, and have those changes reflect, back into the Vagrant session. Meaning we can use Visual Studio Code to do all of our code editing and then use the tools within the Vagrant instance to do the compilation and testing.
This will become clearer, as the demonstrations proceed. But for now, what I quickly want to do is jump into the client directory and open the Bitcoin app. Now, this particular class implements a simple Java console based application and which we actually import, the library that we're going to build, and then use it within this console project to create an application that allows us to take user import from the terminal in the form of a currency and a number of Bitcoins and then do a live conversion of that.
So let's now compile lists using my event and then we'll run it. I'll clear the terminal, I'll jump into the client directory, I'll list its contents. And then you notice here there's a palm dot XML file. So this is all of the project management configuration, that may have new users.
Now, before I compile the console application, I need to use Maven to install our library here. So to do this, I'll jump back into the repository. And I'll skip to branch seven. And then I'll copy the following commands. I'll jump back to the terminal. I'm already in the client directory, I'll paste them.
So in the background, what is happening is that Maven is actually doing an install of this particular library, within the Maven cache, on the Vagrant instance. Now the first time this happens, Maven needs to download and install a number of internal dependencies, for it to work and know how to do the build compile and package phases. This is a one-off installation phase.
So once this is done, subsequent Maven commands will run a lot quicker. Okay, so the installation of our library, and now our local cache on the Vagrant instance has completed successfully. In this stage, I'll now do a full build of the Java console application to do so, I'll run Maven, compile, followed by package.
Again, Maven needs to do some downloading of third party library dependencies, and this case, libraries that are dependent by our console application. And this is really one of the very cool things about Maven, is that it does all this project management for us. And when it comes to downloading third party libraries, it will not only download the libraries that are directly required by our application, but also download all of the transitive libraries. That is, if our application depends on a third party library and that third party library depends on other third party libraries, and so on. Maven takes care of all of this for us.
Keep in mind that this downloading process, only happens the first time you do it with a new directory. So all subsequent Maven commands after this command, will happen a lot quicker because all of the libraries, will have been caged locally. Again, while we're waiting for the downloading to complete, I wanna make it clear that this library here, is the library that we're going to build, using Test-Driven Development workflow in the following demonstrations.
In this particular demonstration, we're importing the outcome of the library, the compiled library and to a Java console project so that we can see how it runs with the user input. Okay, excellent. So the build has completed successfully, and the outcome of this, is that we should have an executable jar and the target directory.
So if we do a directory listing, we get a new target directory that Maven creates for us. In the end, if we look inside there, we can see a couple of jar flops, the top one has been compiled with all of the dependencies required and packaged within it to make it executable and runnable.
So let's now run this. I'll now clear the terminal, and then I'll use the command Java desk job target and then we'll run this file here. Okay, so our Java console app is now up and running. It's asking us for a number of coins, I'll try 100. Then asks us for currency, I'll go with USD. And now this will do a real time conversion. So this is now connecting out to the CoinDesk but coin currency conversion API. So here we can say that 100 Bitcoins, converted into USD dollars is this amount here.
Let's run another one. With 150 coins and into Great British Pounds. And here we can see the respective result. Okay, so that demonstrates the functionality of the Java library that we're going to build it. In this particular case has been imported into our Java console application. And as an end result, when we compiled and packaged it, gave us the ability to run this particular program.
Now in the background, what is happening is that our console application is calling our library and our library is going off to the Bitcoin Coinbase KPI and getting in real time, the currency conversion rights for Bitcoin. So this stage, should now clearly understand what we're about to do and the final demonstrations. And that is, this particular library here, we going to build from the ground up using Test-Driven Development workflow and practices.
So let's begin. I'll exit out of the application, And then I'll navigate back into the Vagrant directory, I'll do a directory listing, and then because we're going to build this from the ground up, I'm going to clear everything out of the Vagrant directory. To do so, I'm going to run the command, ls dot for the current directory, I'm going to pipe the results, which are all of the folders and file names into the grid command, I'm going to do an in view selection and I'm going to map on dot Vagrant.
Now the reason I'm doing this, is I want to delete all files and folders except for the dot Vagrant meta directory, Vagrant users, the dot Vagrant directory to store metadata about the current Vagrant box. In particular, it stores the SSH key that is used to ECCH enter the box. Therefore, if we lose this directory, we lose our ability to ECSH beckon to this particular Vagrant instance. And then finally our pipe the results, through xargs to the remove command, doing it recursively.
Okay. That looks good. Now to confirm that we've still got our dot Vagrant directory I'll use the tree command like so, excellent. So that looks good. We've still got our dot Vagrant meta directory available and that will continue to allow us to Vagrant SSH agents with this box if we were to shut down the Vagrant box and then later resume it.
Now again, you don't need to do any of these, I'm just tearing it down to start afresh so that I can demonstrate how I'm going to build up the solution for you. I'll jump back into the get repo, and I'll navigate to the step one branch. Now, the next thing I'll do, is I'll copy this command here, and then jump back into my integrated terminal and I'll run it.
Now, this is going to set up and install a custom Maven archetype purposely configured to help us create brand new Java 11 in Junit 5 Java projects. So the build and install of this custom Maven archetype has just completed. Now, the next thing we'll do is copy this command here. And this will actually generate a brand new Java 11 in Junit 5 five solution using this custom Maven archetype.
Jumping back into the terminal, clearing the current console, I'll now run the command. So again, the outcome of this is that it's going to build a brand new project, set up purposely for doing Java 11 and Junit 5 unit testing. So in the background, Maven is doing all of the heavy lifting for us.
Now, if you were to do this by hand, that is to set up a brand new, Java living project with JUnit 5 unit testing, doing this by hand would be very time-consuming. And the great thing about Maven, is that it does all of this for us.
Okay, so the previous command has succeeded, in what that has given us is this new directory here. Pre-configured for JDK11 and JUnit 5, unit testing. Now that that is done, we can actually remove the Java 11 JUnit 5 archetype just to keep things simple and clean.
I'll now navigate into our new library project. If you run the tree command, you can see the project directory structure that has been initialized for us. When we ran the previous command. Here we can do a Maven, clean, compile, test, package.
Now again, Maven does some initial downloading of library dependencies used to do the project management aspects of a new project, as well as downloading any third party libraries that the initial source code depends on. As early I mentioned, this is a one-off process. Once this is done the first time, all of the downloaded dependencies occasied locally. Okay, excellent. That build completed.
Now, just to prove my point, If I do another Maven clean in this time we'll just run the test. You'll see that this time the sample unit is ran very quickly. Let's just quickly pause here and understand what we've just done.
Firstly, we installed a new Maven custom archetype which when used, allows us to quickly initialize new project structures and particular pre-configured to use open JDK11 and JUnit 5 for unit testing. We then use this custom archetype to generate our brand new Java library project structure.
So you can see here that Maven uses the following convention. It has a main directory, and a test directory beneath a source directory. The main directory contains all of the source that makes up our application, in the test directory, contains the units.
Okay, in this stage, let's now start beginning the implementation of a Bitcoin library. To do so, I'll begin in the test directory. I'll rename this to be converter SVC test dot java. Next, within this file, I'll update the package name to be conduct cloud academy dot Bitcoin. I'll then change the input to be import assertions. I'll update the class name to be converter SVC test. And then for our first test, I'll create it like so, I'll give it the name get exchange, right? underscore USD, underscore returns, USD exchange, right?
So that's telling us a few things already. We're going to be implementing a method whose name is get exchange rate. It's going to take some state, which is the currency that we want to convert in this case, USD. And we're testing that it returns the equivalent USD exchange rate. We'll use the arrange, act assert pattern, to arrange our unit testing code.
So within a range, what we need to do is instantiate a new object of type converter SVC. And then under act, we'll create our actual implicit variable, And we'll say that on our converter SVC object, we're calling the get exchange rate. And we're passing in USD. And then for our assertion, we use a double, we'll say that the expected return is 100, which for now is just a fictitious value ASS. And then finally, on assertions we'll call the assert equals method, and we'll say expected should equal to actual.
Okay, so let's have first unit test, we'll save it. Clear the current terminal and this time we'll run Maven clean test. Now this test will fail. This is the red phase, it's failed because it's a compilation file. That's expected because we haven't actually implemented this class yet.
So that is our next job. So we'll copy that, I'll delete this file here. I'll rename the calculator class, like so. I'll update the package to be the same as the test. Our class name needs to be updated. And then within the class, I'll start from scratch. I'll create a new public constructor. And then we're ready to implement our first method, which we know by convention because of our naming standard, will be called this. It will be public int, and it's going to take a string, which is the currency. And then we'll give it an implementation.
So in this case, it's just going to be a fictitious implementation for now. We'll say if the currency equals USD, then we'll simply return 100. Now we need a return outside of the statements, so in this case, we'll just return zero here.
Okay. We'll now rerun our test. And if all goes well, our first unit test will pass. As it has. So in this case, we've completed the green phase. So remembering our red, green refactor workflow. Okay, so we're back into the reflector phase which is we go back to our unit testing class and we extend a bit more.
So in this case, I'm going to copy everything. And we going to test. And our second unit is Great British Pounds. We changed the imported currency to GBP And then this time I'm going to set it 200. I'll save it. I'll rerun out this before we do the implementation. This will fail. This is the read phase.
Jump back into our implementation now. And this time I'm going to extend it by adding in the implementation for Great British Pounds. And we'll update the return value to be 200. We run out test. And our test has now returned to green.
Okay, so we'll return back to our unit test, similar do one more test here for the get exchange rate method. This will be for yours. And for this one we'll return 300, save, run the tests, this will go read, within two hour implementation, change US to Euro and we'll return 300, save, excuse the tests again, and this will go to green.
Excellent. So we've completed the first initial implementation for our get exchange rate and all of our tests now passing. The next thing we'll do is we'll move onto, the second method that we need to implement. So now that you've got a good understanding of the Test-Driven Development principles in workflow I'll now just copy and paste the implementation for our convict Bitcoins method just as speed things along.
So here we can see, we're implementing a new unit test, this going to test a method called convert Bitcoins. It's going to pass in the state who convert one Bitcoin to USD dollars and it's going to expect a USD dollar amount returned. We're acting on the same object. And this time we're calling the convert Bitcoins method. Passing a new USD for the currency and one Bitcoin. And we expect the following value returned, and then we do the assertion.
So I'll save that, run test, I'll go red. And now we'll do the implementation. I'll pass on the implementation again just to speed the demonstrations along. The implementation is fairly basic. We have a new method called conveyor Bitcoins at Tycksen two parameters, the first of which is the currency, and the second of which is the number of coins. We then call the get exchange rate method to get the current exchange rate for their currency. And then we just do some simple math. We multiply the exchange rate by the number of coins per stand, and then we'll return the dollar amount bag.
Okay, we'll save that, run the tests. And excellent. All tests are now passing. So we'll go back to our unit test file and then we'll expand this a bit more. The next unit is, as much as I'm as the previous one but this time we're going to do the conversion on two Bitcoins.
So we'll save that. In this time the new test goes straight to green which is an expected result and proves that the maths being performed, in our new method, aren't they correct.
Finally, back within our unit tests, I'll paste in the following extra Ts, which tests the conversions to Great British Pounds and also to Euros. I'll save those, and I rerun the tests. And we now have nine unit Ts, all going to green. So this is a great start. And it shows you clearly the basic principles of the Test-Driven Development, red, green, refactor workflows. So go ahead and close this lesson and I'll see you shortly in the next one, where we refactor our implementation to make real calls to the CoinDesk, but coin API.