The course is part of these learning paths
Continuous integration is the first step toward a completely automated development, deployment and operations pipeline. It helps to mitigate integration issues, and catch known issues early via automated testing. If you're new to continuous integration, this course is a great place to start. We'll explore the various tools, technologies, and vocabularies surrounding the continuous integration ecosystem, as well as introduce you to the key tools of the trade that will enable you to get a headstart in your burgeoning DevOps career.
You will gain the following skills by completing this course:
- How to set up your development environment.
- How version control works.
- How to begin implementing testing in your environment.
- The why and how of database schema migrations.
- What Jenkins is and why you should care.
You should take this course if you are:
- A newcomer to the DevOps or cloud world.
- Looking to upgrade your skills from a conventional software development career.
This Course Includes
- Expert-guided lectures about continuous integration.
- 57 minutes of high-definition video.
- Solid foundational knowledge for your explorations into DevOps.
What You'll Learn
|Video Lecture||What You'll Learn|
|What Is CI?||What continuous integration is and why it matters.|
|Creating a Development Environment||How to set up your development environment.|
|Version Control||How version control interacts with the CI process.|
|Testing||How to mechanize your testing with CI.|
|Database Schema Changes||How to implement and run database schema changes.|
|Introduction to Jenkins||An overview of Jenkins and how to utilize it within your CI process.|
If you have thoughts or suggestions for this course, please contact Cloud Academy at email@example.com.
Welcome back to Introduction to Continuous Integration. I'm Ben Lambert and I'll be your instructor for this lecture.
In this lecture, we're going to talk more about testing. And what sort of tests need to be included in a continuous integration process.
Being an introductory course, we're not going to go into depth on testing. Testing is a very broad topic. And it's also one that's filled with a lot of different opinions.
There are a lot of different types of testing and they all have their place. For this lecture, I want to introduce you to a few of the types of testing that will be involved in the CI process. And I want to talk about why testing is important. And I want to start with the why first.
At a certain point in your career, you'll probably have heard this argument a few times. And it goes something like this: Sure, testing is great, but we’re on a tight deadline, so we don’t have time to write tests.
Now if you're a developer and you're the one really under that deadline, it might seem like a reasonable way to think. And maybe if it's a small enough project with minimal complexity and there are no real implications for outages, then yes, that could be the case.
However the more complicated the project or the more mission critical code becomes, the greater the need for testing that comes. If bugs in the code cost money in the form of outages, discovery, or rework, then you can't afford not to test your code.
For example, would you get on a plane and take a cross country flight if you knew that the operating system of the plane was just patched? And the only testing that was done was done locally on the developer's laptops just before they deployed the code.
Now personally, I wouldn't get within 10 feet of that plane. And I certainly wouldn't take a shuttle ride into space with untested code or use a self driving car with untested code. And for that matter, I really rather not even enter my credit card into a website with untested code. And that's because it is really easy to introduce bugs and security holes.
So why should we test? The sooner useful tests are run, the sooner problems are found. At a minimum, when it comes to continuous integration, we should be running our unit and integration tests. However I also recommend anything that can help improve code quality. By this, I mean things like linters which are code quality assessment tools. Also static code analysis tools for security auditing. Let's start with talking about unit testing.
Unit tests are the most granular level of testing since they test a single unit of code. They're responsible for determining if your classes, functions, and methods behave the way they're expected to. Unit tests need to be fast so they can be providing developers with immediate feedback. If a developer runs the unit tests before and after they make changes, they'll know if a change that they made caused something to break. By this I mean things like databases, file systems, the network or anything of that sort. Now the reason for this is two part. First it ensures that the tests run very quickly. If the tests take several minutes to run, then eventually developers will stop running them. Developers need to be able to continually make code changes and get immediate feedback on whether those changes broke something.
And the second reason is that we're trying to determine if the logic in the code is performing correctly. So at this level of testing, we're going to assume the external resources worked correctly. Those things will be tested at a higher level. Let's take a look at an actual unit test. First, let's check out the functionality we want to test. Here we have an user class. And we have a few methods on it. We have a method to normalize a user's name. One to upper case the name and another to create an URL slug. Let's look at how to unit test and see if we can verify that these methods behave how we expect them to. In this example, you can see that we have a user unit test class, and some test methods on it. We start with the setup method. This is where we can run code before each test so that we can ensure that we've initialized anything that we may need to. In this case, I'm sending a user variable to a new instance of an user object. And then we have a few tests to validate that some of the methods on the user class are working correctly. First we test the uniform name method. Then the capitalize and finally the slug method. And if we run this, everything passes.
So testing at this level provides a level of trust that the individual functions are doing what they're supposed to. It's not uncommon for developers to struggle with creating good unit tests. And usually this is because the code wasn't written to be unit tested. If your code directly instantiates things like database connections inside your classes, then you may have to do some code re-factoring to use things like dependence injection. Here's an example in C-Sharp. In this example, look at line 10. We're instantiating a database connection. Which means we're now creating a dependency in our code for that database.
Instead of this, we should be using DI, which is dependency injection. Now here's an example with dependency injection. If you look at line five, we're using an interface in place of the class and the constructor now takes that interface as an argument. Now we can use just about any inversion of control container that we want to handle the mapping between our interfaces and our classes automatically. In this example here, I'm using an inject to map online 67, our interface to our class. You may be wondering how this actually helps. The reason is your code is no longer bound by this hard coded dependency for the database. In its place, we have an interface that we can implement with either a real or a fake version depending on what we want to do.
So now if you want to unit test this, you can use a fake version of the database connection as long it implements that interface. What if we wanted to test the interaction between components and see how things work with an outside resource such as a database? Well this is where integration testing comes in. Let's take a look at an integration test. In this example, we're testing the homepage of the website. If you look at the test index method, you'll notice that it calls the get method on the client object, which is basically a fake web browser. So it calls the forward slash route which is our homepage and it checks to make sure we have a 200 status code.
So if we look at the test index contains text, it does basically the same thing except it ensures that some specific text is returned. You can see that the unit tests were very different. They focused on the more granular level and were concerned with the logic of the unit. Whereas these integration tests are executing code across multiple layers of our application. Because integration tests are allowed to interact with external resources, they tend to be slower than unit tests and are probably not suitable to have developers running them locally. Or at least not frequently. These two types of tests are in my opinion the minimum recommendation for continuous integration. However I also like to recommend a few nonfunctional tests as well. The first are code linters.
I talked about it before. Code linters are quality assessment tools and they can help to improve code quality by identifying known issues. The second are code coverage tools. Now code coverage checks to see how much of your code is tested by your test. I mentioned code coverage tools because they can be useful. However they also can be a bit of a rabbit hole to fall down. Now maybe your code base requires 100 percent coverage and maybe that's doable and useful for you. However it should not be a rule. And the last type of test that I like to recommend is source code analysis tools.
These types of tools can check for known security holes and identify the use of dependencies that are known to be vulnerable. This level of security testing is for developers to catch the very low hanging fruit before code goes on to more complete security audits in the continuous delivery process. Okay, let's summarize. Testing is important because it catches problems early while they're inexpensive to fix.
And when it comes to continuous integration, you want to at a minimum run unit tests on each command. And depending on their speed, you want to run integration tests on each commit too.
In our next lecture, we're gonna talk about database schema changes. Alright, let's get started.
About the Author
Ben Lambert is a software engineer and was previously the lead author for DevOps and Microsoft Azure training content at Cloud Academy. His courses and learning paths covered Cloud Ecosystem technologies such as DC/OS, configuration management tools, and containers. As a software engineer, Ben’s experience includes building highly available web and mobile apps. When he’s not building software, he’s hiking, camping, or creating video games.