Welcome to the Deep Dive, where we unpack complex ideas to give
you those really insightful takeaways.
Today we're digging into something pretty powerful, but
maybe a bit misunderstood sometimes in software
development mocks. Yeah, that's right.
And our our guide for this is Marco Tulio Valente's Software
Engineering a Modern Approach. It's a really solid resource,
gives you practical insights, especially, you know, when you
get into unit testing. Absolutely.
So our mission today is pretty clear.
Let's really get our heads around what mocks are, why
they're actually quite essential for good software development,
and how they solve this really common problem you run into when
you're trying to write tests that are, well, efficient and
focused. We want you to walk away feeling
like you really grasp it without getting overloaded.
So let's just jump in. OK, so when you're building
software, you hit this challenge pretty early on, right?
You want to test the small parts, the individual pieces of
your code. That's that's basically what a
unit test is for, checking one isolated component, making sure
it works like it should. But what happens when that small
unit, that little component, actually depends on something
you know, bigger something outside itself?
Yeah, that's that's a classic scenario.
The source Valente's book uses a great example.
Think about a class called book search.
Simple enough. OK, it's job.
It's got a method maybe get book and it needs to find books.
But here's the kicker. To do that search, it doesn't
just look inside the application, it relies on
something called a book repository a.
Repository. So like a database?
Sort of, but in this example, think of the book repository as
representing an external service.
Maybe it's a REST API somewhere else on another server entirely?
OK, like calling out over the network?
Exactly. So when get book runs, it
actually reaches out to this remote repository, grabs the
results maybe in Jason format, which is pretty standard, and
then it uses that data to create a Book object inside your
application. Right.
And I can already see where this might get tricky.
If you want a unit test that get book method, letting it talk to
a real remote server every time you run the test, why is that
actually a problem? Why not just let it connect?
Well, there are two really core problems with that approach,
especially for unit tests. The book lays them out clearly.
First one is what we call scope expansion.
Scope expansion meaning. Meaning a unit test is supposed
to focus on just one small thing, right?
It's whole point is isolation. Verify this component, the book
search class in our example. But the second you let it talk
to an external service, boom, your test scope just exploded.
So you're not just testing book search anymore?
Exactly. You're suddenly testing the
network. Is the remote server up?
Is it working correctly? The test loses that crucial
isolation. It's not really a unit test in
the true sense anymore. OK, that makes sense.
It muddies the waters. What's the second big issue?
The second one is a big performance hit.
I mean, think about it, accessing a remote service that
involves network calls may be delayed.
Things can be slow. Yeah, definitely slower than
just running code locally. Way slower.
And that goes against a fundamental idea of good unit
tests. They need to be fast.
Super fast. Ideally.
Remember the first principles for testing.
The F is for fast. If your tests take ages to run,
developers just won't run them as often.
Right, you lose that quick feedback loop.
If it takes minutes, you'll hesitate.
If it takes seconds, you run them all the time.
Precisely. Slow tests really hurt
productivity and confidence. You want to catch bugs quickly,
not hours later. OK, so we've got this clear
challenge. We need to test our little book
search class, but it depends on this external book repository
which is slow and kind of outside our direct control for
the test. How do we keep our unit tests
fast and isolated, true to that unit idea without just, you
know, ignoring the dependency? And that brings us neatly to the
solution mocks. It's quite an elegant idea
actually. OK, tell me about mocks.
A mock at its heart is basically an object you create
specifically to pretend to be the real object, but only during
your tests. It emulates the real things
behavior, but in a controlled way.
It's worth mentioning the author here.
Valente uses mock and stub pretty interchangeably.
We're focusing on that practical idea.
A stand in for test. A stand in.
OK, so how does that apply back to our book search example?
Yeah, how do we use a mock to get around that slow book
repository? It's pretty clever in practice.
Instead of letting book search talk to the real remote book
repository, you create a fake one.
Let's call it Mock Book repository.
Now the crucial part is this. This mock book repository looks
exactly the same to book search as the real one.
It implements the same interface, the same methods.
So book search doesn't even know it's talking to a fake.
It has no idea, it just calls the methods it expects.
But the inside of the mock book repository is totally different.
How so? It's super simple.
It doesn't make any network calls, it doesn't talk to any
remote server, it doesn't wait for anything.
Instead, when you call it search methods, say with a specific
ISBN like 1234, it just instantly returns some
predefined data, like maybe a fixed Jason string that
represents the software engineering book.
OK, so it's hard coded basically.
Pretty much for the test's purpose.
For any other ISBN, maybe it just returns a null book string.
You control exactly what it returns instantly.
The source mentions maybe storing these predefined strings
in like a a book construction class just to keep them tidy.
Got it. Consistent, predictable data.
Exactly. So then your test set up your
book search test. It creates the book search
object, but instead instead of giving it the real repository,
it gives it this mock book repository and.
That lets the test run without hitting the network.
Bingo. The test get book method runs.
It calls the mock, gets instant predictable Jason back and does
its thing. The whole test runs super fast,
completely isolated, no network involved.
That is brilliant. OK, so we've made the test fast.
We've isolated it from the external world, focusing purely
on book search. But that brings up a really
important question. If we're using this mock, this
standard that just gives back canned data, what are we
actually testing? What requirement are we checking
here? It's obviously not testing the
real connection anymore. That's a really key point to
understand and you're spot on. The test when using a mock like
this is not testing the access to the remote service.
That's just not its job, OK. Testing whether you can actually
connect to the real remote server and get real data, that's
a different kind of testing. Maybe an integration test or a
system test. It's important, but it's outside
the scope of this unit test. So what is this unit test
verifying then? What it is verifying, the core
requirement it's checking is. This.
Is the logic for creating a Book object from a Jason document
working correctly? So it's about the transformation
within my code. Precisely, you're using the mock
to guarantee you get predictable input that specific Jason
string. Then you're checking if your
book search code correctly parses that Jason takes the
right data out and successfully creates the book object with the
right properties. I see.
So it's all about testing the internal logic, the processing
inside the book search class itself.
Given a known input, We're taking the external dependency
out of the equation for this specific test.
Exactly. You're ensuring your own codes,
transformation, parsing and object creation logic is sound,
regardless of what the outside world the real service might be
doing at that moment. It gives developers huge
confidence. That makes so much sense.
It's like saying I know the input will look like this thanks
to the mock. Now let me make absolutely sure
my code handles this input correctly.
It isolates the responsibility. Right, and you can refactor your
internal book search logic, change how it parses things, and
be confident your unit test will tell you if you broke that
internal logic without worrying about network glitches or the
remote service being down. And I guess you can easily
extend that mock, right? Add more predefined books, test
edge cases with different Jason formats that might return, test
more fields, all without touching the real external
system. Absolutely.
You can make the mock return different canned responses to
test various scenarios within your book search logic, making
your tests incredibly robust for the code you actually control.
OK, that's a real aha moment. For me.
It's about focusing the unit test laser sharp on the unit
itself. That's the power of mocks in
unit testing. Well, hopefully this deep dive
into mocks has really clarified how these these clever emulators
help developers write tests that are faster, much more reliable,
and really focused on the actual unit of code.
It's definitely a key technique for, you know, staying
productive and confident when you're building complex
software. Understanding how to isolate
things properly really helps you build faster and with fewer
nasty surprises. Yeah, absolutely.
Thank you for joining us on this deep dive today.