Introduction to Testing Software

A free video tutorial from John Thompson • 500,000+ Enrollments Worldwide
Spring Framework Guru - Best Selling Instructor
24 courses
364,421 students
Learn more from the full course
Testing Spring Boot: Beginner to Guru
Become an Expert Testing Java and Spring Boot Applications using JUnit 5, Mockito, Spring Boot, and More!
20:43:42 of on-demand video • Updated January 2025
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.