Introduction to Testing Software
A free video tutorial from John Thompson
Spring Framework Guru - Best Selling Instructor
4.5 instructor rating • 17 courses • 195,366 students
Learn more from the full courseTesting Spring Boot: Beginner to Guru
Become an Expert Testing Java and Spring Boot Applications using JUnit 5, Mockito, Spring Boot, and More!
16:43:20 of on-demand video • Updated November 2020
- Learn Important Concepts behind Test Driven Development
- Understand different types of testing - Unit, Integration, Functional
- Master the JUnit 5 Testing API
- Easily integrate Mockito with JUnit 5 Tests
- Migrate JUnit 4 tests to JUnit 5
- Test Spring Framework Applications with JUnit 5 and Mockito
- Write Unit Tests with Spring MVC Test
- Learn to use testing features of Spring Boot
English In this video we are going to do an introduction to testing. I just want to look at software testing in a very general sense. Kind of get you feet wet, establish some baseline terminology and talk about testing in general. So first off, why do we want to write tests? A lot of people complain that writing all these tests takes a lot of time. Why should we even bother doing it? Junior developers especially they just want to get right into the meat and potatoes and write that code and bang it out and make try to run it and make sure that's doing what it's doing so we can put in print line statements to see what's going on. That is definitely a junior developer type thing. The reason you write tests is it is going to improve your software quality over all. It absolutely will help the quality of your code. It proves that your code is going to be doing what you think it should be doing, So it's very important. I've made mistakes and it's so easy to usually greater than sign instead of a less than sign. Very very very easy to mistake to do. So tests are going to catch that so you when you're gonna be testing conditions like that. The goal of having Test coverage is you want to and the good old fix one thing and break in another. So when I was more of a junior developer all the time I'm gonna be absolutely honest there all the time going fix one thing and break another and then you're just chasing down bugs and that is because I didn't have test coverage. When you have good test coverage, it's very easy to change one thing and then break a test and see the ramifications of your change further down the line. This is really why it is widely accepted as an industry best practices to have solid test coverage. As you get into larger shops, software development shops with more developers and more senior level developers, you will see that tests become more and more important and it also allows you to change existing and I put in there working code with confidence. So as you develop software, it's very common for that software to evolve, business requirements to evolve. You look at things and go oh this is probably a better way to do that. This is more efficient to do it this way. So having test allows you to change existing and working code with confidence. Now let's talk about some testing terminology. Code Under Test: This is the specific code or could be an application that you are testing. A Test Fixture: This is right out of the JUnit documentation. A test fixture is a fixed state of a set of objects that used as a baseline for running tests. The purpose of a text fixture is ensure that there will be a well known and fixed environment which tests are run and repeatable. Now this can include input data, mock objects, loading a database with known data. So it basically sets up your test so you have code under test and then a fixture which is going to kind of set up the environment for your code to run within. Unit tests/Unit Testing: This is something that is largely abused I should say. People use the unit test and quite often they're not talking about an actual unit test so I want to be specific on this. This is code written to test code. It is designed to test specific sections of code. Percentage of line code tested is code coverage. So there's another important term is code coverage. Ideally your coverage should be in the 70 to 80% range and I say ideally there are some people that are very detail-oriented and they want to have a hundred percent coverage and when you start getting above this and this is a really ballpark figure. When you start getting above the 80% range, you start getting a diminishing value of returns on your test. So there's something very small very minor. You may or may not want to spend time testing that especially if it's something very obvious. So that's why I'm saying in the 70 to 80% range and this is not a hard fast rule. You could very easily say hey so when you say that it should be at least 85%. I'm not going to argue with that so I'm just saying this is a very ballpark range. Your unit test should be unity so they should be very very small and execute very various my fast. By this they should be very targeted. They should be targeting a very specific thing. They're going through and testing a lot of scenarios. You might want to take a step back and rethink things. Either your test is wrong or your code is wrong one or the other. A unit test should not have external dependencies and I said ie there, no database, no Spring context, that shouldn't be talking to web service, that shouldn't be talking to message broker. Unit test should be completely independent. I will say and mock objects I guess arguably okay but definitely if you are bringing up an h2 in-memory database that is when you're doing something like that you're getting into the realm of an integration test. These are designed to test behaviors between objects and parts of the overall system. So this is different from a unit test which is usually going to be focused on a specific object a method call within an object that's what a unit test is going to be targeting where an Integration Test is going to be testing behaviors between objects or maybe even multiple objects. So it is a larger scope. They are more complex and now with integration tests you can actually bring up the Spring context, bringing up databases, message brokers, call web services so that there's a lot more going on. This means that they are gonna run much much slower than unit tests. Now Functional Test, is another area that it's largely I think abused and people do vary in terminology here. Some people call these a number of different things but functional tests are primarily gonna mean that you are testing a running typically deployed application. So you might have a test environment that you've deployed to and you're gonna run into functional tests so you're gonna do I said functional touch points. You might use a web driver to drive a webpage, you might be calling web services from the deployed service. You might be sending and receiving messages from a broker. So functional tests are gonna be a lot higher level that you're gonna be testing and obviously since you've actually compiled everything and deployed to a known environment they are very very overall slow. So as far as speed, keep in mind unit test very fast, integration test not as fast depending on what you're doing and the slowest tests are going to be your functional tests. And this leads me up to the next thing that you need be considering here is this testing pyramid. You'll see this represented in different ways. The concept that I want to convey here is the majority of your tests should be unit tests. So that's why I have the unit test down at the bottom of the pyramid. That should be your overwhelming majority. You want small, fast, lightweight tests and I've seen a lot of people make mistakes on those especially spring developers they are awful at those to be honest. They bring up in the Spring contests for a lot of things they absolutely do not need it for. But what happens is that spring contest starts over and over and over and when you start getting hundreds of tests, your test now it goes from running in a few seconds to maybe a minute to five ten minutes and it just slows down the build because you have a bad work practice. You're doing tests but you're doing integration tests when you really don't need to be. so that's why I want to stress that you want very lightweight unit tests. They are very targeted. They are targeting very very specific things. Now integration test, they do have their role. You do need to test interactions but you're going to be testing at a much more granular level. So you're going to be testing interactions. You don't have to test every getter and setter integration test. That is not the appropriate place for an integration test. You gotta make sure that a message goes in and out. You have a unit test to test for all the properties getting set. And then finally, we get down to the Functional Test. These are the smallest in number also typically the least detailed. So they're gonna be it's the service alive. I'm being a little sarcastic there but they are going to be much less detailed than the other test. It is more some people do put details in there especially for different web services. Sometimes they are just a sanity check to make sure that the service is up and returning back the minimal information. Different people at different philosophies around the functional test but conceptionally what I like to see is most of the detailed business logic being tested inside of unit test. Functional tests are gonna have a much higher level. Integration tests are going to be somewhere in between. But the quantity of test the absolute bulk of them should be unit tests that are going to run fast. You should have hundreds if not thousands of unit tests and like dozens hopefully less than a hundred functional tests. So just give me idea and there's no hard rules here. It all depends on your application and its requirements. Now the next thing I want to talk about is the importance of clean code. I hit on this and if you join my email list, I actually have an email called but my two thousand line method tested just fine. And I absolutely debunked that. That is absolutely impossible. First of all, I've seen this way too many times where a single java method has over two lines of code and it was absolutely obnoxious and you can't test that properly. There are too many conditions happening within that, there's just too much stuff going on within that single method that needs to be broken down, refactor it into smaller more manageable chunks.Now my second ball point there is quality code starts with quality code and you need to be following good coding practices. I can't teach everything in this course. We are specific specifically on testing not object-oriented programming or good patterns or anything like that but you want to be doing the Solid OOP. Just good OOP in general. Gang of four design patterns. You need to understand those. Excellent book and I'll put this in the bonus section. I'll put a link up to Amazon there. A clean code, it's an older book. It's by Robert "Uncle Bob" Martin. He's an iconic programmer, very smart guy. Really a lot of great tips in there like Method shouldn't be more than a screen full. So if you're stringing it more than a screen full of code, you're probably doing too much in that single method. So just a little rules of thumb like that very pragmatic stuff so it's an absolutely great book. If you're newer developer, I cannot recommend that book enough. so my final bullet point there is test coverage cannot overcome poor coding practices. So think about going back up to 2,000 line method. Allegedly tested just fine. No you cannot write a unit test so that's gonna cover everything there. I want to talk a little bit about agile testing methods. Test-driven development, we are gonna see this coming up in the course. First you write tests and then you write code to fix the test and then you refactor the code to clean it up and then improve. So a very simple process. What this does test-driven development actually gets you writing your tests first so you have a failing test and then you go and implement it so it makes you think a little bit more about what you're doing which are targeting with the tests and at the end of the process you've already got the test written so when you need to refactor or something you're good to go. We will also be looking at Behavior Driven Development. Now this is very similar to test-driven development. You can actually intermix them a little bit. This is going to describe the expected behavior of software so it's a little more expressive. I really like it and when you're using the proper tools there it's actually quite nice. I do like using Spock which is a behavior driven framework. It's quite nice. A lot of times you are going to express your BDD test as when and then then block and then I expect this or you could be saying given a condition when this happens and then I expect this result. So just having that logic there does help you visually structure your test better when you start thinking about how you're going to write the test. Now which is better to use? I say use both. It depends on what you're testing. If you look at stuff that I write, I write in a number of different languages. Sometimes it's depending on what I'm using and I feel it's a better tool. Sometimes it's on my mood, I'm gonna be honest there. But I do use those and sometimes it's really depending on exactly what you're doing. So I don't have a hard rule thumb there. Now just a couple other things that we need to talk about because we will be seeing these in the course as well. Testing components: Mocks. This is a can be a very confusing area. I'm not going to get into too deep right now. A mock is a fake implementation of a class or an object that you're going to be using in tests. It is like a test double. Think about data source. So everybody taking this course should be a Java developer so we've all seen a JDBC datasource. A mock would be an implementation of that so rather than getting a data source from MySQL using the jar for that to get obtain a data source for it, I could write my own implementation of it and give back responses to my class that I'm expecting. So mocks can provide expected responses. They can also verify expected interaction. So a couple of important things about mocks. Now spies they are going to be like a mock but a real object is use. I kind of think the spies are more like wrappers so that they wrap up the real object so you can say I want to override something on this object with a spy but at the same time you can also say we want to allow the normal behaviour trigger. So spies are a little bit confusing but the real object isn't involved and we will be looking at both of these closer coming up in the course.