- In the previous section,
we saw how to mock everything in the module.
You just say, jest dot mock,
and Jest will automatically create a mock version
of every function in that file.
So that works for any free function,
any global function defined in that module,
plus constructors in classes, plus methods in those classes.
So basically every function in that file will be mocked
with kind of like, a do nothing implementation.
And that's fine.
That's the easiest way to mock a module.
It's also possible to define manual mocks.
So rather than having automatically generated mocks,
which are basically empty,
you can define your own kind of stub implementation
with methods that actually do something more useful.
So that's what we're gonna look at in this section.
So what you do is you create a folder,
called underscore underscore mocks underscore underscore.
In there, you implement mock or stub versions
of the functions with some kind of useful behavior,
instead of the real class.
And then, you just basically tell Jest to mock the module.
And because it looks
for the underscore underscore mocks folder.
And because it finds it, when you say Jest dot mock,
instead of generating automatic mocks,
which are kind of empty,
Jest will use your mocks, instead.
It'll use your mock functions instead of the real functions.
Okay. So we're gonna see how to do that.
If you go into the mocking classes, manual mock folder.
Okay. So that's the folder you want to go into.
If you open that folder in the code editor,
it looks like this.
Inside here,
same classes as in the previous couple of sections,
I've got a low level rest client.
This is the real implementation of rest client, where,
which basically calls into a rest service using Axios.
Okay.
So when I'm unit testing my other class product manager,
I don't want these real functions to be invoked.
Okay? Because unit testing is meant
to focus on one class at a time.
So what I'm gonna do is I'm gonna
basically define mock versions of those functions.
And I've done that in the mock folder.
Have look in the mock folder,
or underscore underscore mocks underscore underscore.
I've defined mock versions of these functions,
have a look in here.
And these are the functions that would be used instead.
So I've hard coded my kind of dummy or my stub rest client,
so that the constructor just output a message to say,
hi, this is the mock version of the constructor
that's been called.
I've got some hard coded data here.
Last week, we went skiing in Austria and it was brilliant.
I wish I was still there, but there we go.
So anyway, skis, that's one product.
There were 500 pairs of skis in stock, sorry.
There are hundred pairs of skis in stock.
And each pair of skis cost 500 pounds.
There are five pairs of boots in stock,
and each pair of boots cost 200 pounds.
And there were three goggles in stock,
which cost a hundred pound.
Well, that's quite expensive.
But that's a hard coded set of products.
Imagine that's pretend data that we've got back
from the rest service.
So the delete function,
when the delete function is called a rest client,
it'll call this kind of manual mock version
just to say, hello, this is the manual mock version
of the delete function.
The update function, again,
it doesn't actually call using Axios.
It just does something, a mock implementation.
The get All function is a bit more interesting.
It's meant to return a promise,
but it turns a hard coded set of data.
It says, this is the mock version of the get all method,
which is going to dissolve.
In other words, succeed,
as if the rest service had actually returned that data.
So rest client products,
that's the products I just showed you up here.
So it'll return that as hard coded data,
rather than an actual rest service call.
That's the purpose of defining manual mocks is
that you can kind of give it some sensible default data
for the context of your unit tests,
if you wanna get the product by ID.
Okay, so what it does, it basically,
it sees if my array at the top contains the ID.
When you call the find function,
it either returns a product or it returns null.
So if the product matches, then it returns that product.
And if the product, basically if the ID is invalid,
then it rejects.
So this promise will either succeed with the product
that's being requested,
or it'll reject with an error to say,
unrecognized product, basically invalid ID.
So these are the mock versions
that are going to be used
when my product manager has a rest client.
And it'll be used in these mock versions
rather than the real functions,
or rather than the automatic mocks.
These are a bit more work
but may be useful
to provide some kind of testable behavior
that you can make use of.
Okay, so we've just seen the mock folder.
And I've just shown you the manual mock implementation,
sometimes called stub of the rest client module.
So we can now use those mock functions in our tests.
Okay. So here's a simple test.
Have a look at this.
On the first line,
I've imported the product manager library
'cause I'm going to be, you know,
using that class.
Second line, I've imported the rest client module,
and then I've mocked the rest client module.
In this simple test here,
when I call the,
when I create a product manager object,
let me just remind you about that.
When you create a product manager,
the first thing it does is
to basically create a rest client.
Now that is going to call my stub version
of the rest client constructor.
When I create a product manager,
it's gonna call the rest client mock constructor.
It's gonna call this constructor up here.
Okay, so I should see, displayed on the console,
a message to say, the manual mock
for the rest client constructor has been called.
That should appear when I run the test.
Okay. So we mock the rest client module.
So it'll ordinarily, it would create, you know, empty mocks
but just looks and sees
that I've got an underscore underscore mocks
underscore underscore folder.
And instead of generating automatic mocks,
it uses my manually implemented mocks, instead.
In other words, it'll use the mocks
that I've defined in here,
rather than generating automatic empty mocks
and use my stub functions instead.
So when I create a product manager object,
it calls the rest client constructor internally.
It calls my mock implementation
of the rest client constructor.
So when I run that test,
that's how you run this particular test.
What you'll see, the important thing here, is this bit.
When I created my product manager,
it created the rest client.
It called my mock version of the rest client constructor,
and my mock client constructor displayed this message,
manual mock rest client construct a cold.
Okay, and it shows me it was called
in my mock implementation of rest client.
So that's that proves the point.
And we could end it there actually,
but I'm gonna go on with the couple of more examples,
have a look at this.
I'm gonna invoke the remove products method.
So when I create the product manager,
and then I say remove products,
when you call remove products,
now let me just remind you how that method works.
When you call remove products on the product manager,
it activates through the list of products,
and it basically calls rest client delete.
So if I pass in three ideas here,
it'll call the rest client delete function three times,
and it's gonna call my mock version of that function.
So my mock delete function is gonna be called three times.
So three times,
we should see this console message appearing.
Okay, to say, you know, deleted product one o one,
deleted product one o two, deleted product one o three.
Okay, so I call remove products
on the product manager object.
It will call the delete method three times
on my rest client, on my rest client manual mock.
So I should see that, you know,
when my mock version of the delete method is invoked
basically when this mock version,
my mock version of delete is invoked three times,
I should see this message appearing
in the console when I run my test.
So there we go.
But first of all, it created the product manager,
which created the rest client.
And there's my mock rest client constructor.
But here are the three calls to the delete function
to delete product one o one, one o two, one o three.
Okay, so right in manual mocks,
gives you a chance to have some more substantive tests
in your higher level, if you want to.
Okay. What about this one?
I've created the product manager,
and I'm trying to get the stock account, right?
Okay, so I need to remind you
how the get stock count method works first.
So in my product manager, get stock account.
It gets all products from the rest client.
That's going to invoke my mock version of get All.
What does that do?
The mock version of get All and basically returns.
Well, first of all, it displays a message to say,
hello, this is the manual mock version
of the get All function, and it returns these products.
So it mocks this return from rest service effectively.
It's going to return, well,`
I've got 10 pairs of skis in stock.
I've got five boots in stock.
Five pairs of boots, hopefully, and three goggles.
So all together, if you add these stocks together,
that's 18 products, isn't it?
So when I call get stock level on those products,
the result that should come back,
it should say there are 18 products in stock.
So when I call get stock count on that hard coded data,
the result I get back for my get stock count should be
based on that hard coded ski related data
that there were 18 products in stock.
So call get stock count on product manager.
It calls the rest client.
The rest client returns those three products,
totaled 18 in stock.
We verify that the,
my stock count method has worked properly.
Now, I actually found my test quite handy here
because when I was writing my get stock count method
originally, I basically got the algorithm run.
So my unit test actually helped me to realize
that I'd implemented my get stock count method incorrectly.
So unit testing actually helped me to write this demo
which is quite ironic, I thought.
So we can run that test.
And of course it works and it shows
that my get all method was invoked in my mock API.
I mean, that's just there for proof of concept, really.
The main thing is that it be turned 18
from my get stock count method.
So three additional tests in the test suite,
the get stock value.
What we just saw was the get the stock count.
There's also a test to get the stock value.
You know, the total value in pounds of my stock.
To get the price of a product with a valid ID,
and to get the price of a product with an invalid ID.
Okay, so let's take a quick look at
how those tests work is gonna be the same idea as before.
So in my test, let's spool down.
So this method here get stock value works.
Create the product manager,
which creates a dummy rest client.
When I say, get stock value, get stock value.
Well, the first thing that get stock value does
is to get all products.
Okay?
And as we know in our mock version of get all products,
it returns these products here.
And if you actually add the numbers together,
if I've got 500 skis at 10, sorry,
10 skis at 500 each, that's 5,000 pounds.
I've got five boots of 200, that's another thousand.
So that's 6,000 total plus three times a hundred.
So that should be the total value should be 6,300.
From that stock that gets returned,
from my get All method when it returns those products.
When it returns those products there,
in my algorithm to calculate the total stock value.
I think the number should that come back as 6,300.
Okay, so when I call get stock value,
the result I should get back should be 6,300
if my code in that method is correct.
My product manager also has a get price of method.
I give it an ID, and it'll tell me the price
of that product.
So let's have a look at that.
Get price of, it takes an ID.
It calls the get by ID method on rest client,
which will either return a product or an exception.
So if the ID is valid,
it'll return the product there, basically.
When the product is when the promise is resolved,
that would be the product that I was looking for.
Product one o one, I return the price.
But if the idea is invalid,
then it becomes a price of zero, indicating invalid product,
I don't know the price.
So let's get price method.
If I give it a valid ID, it should return the price.
If I give it an invalid ID, it should return zero.
So my unit tests for both of those scenarios,
if I pass in a valid ID, one o one is valid,
it'll tell, and that's the skis, by the way,
it should give me back a result of 500.
If I pass in an invalid price
and only follow the logic through,
then that should result a zero to indicate that,
you know, the product was invalid.
So right then manual mocks takes a bit more effort.
You know, you basically have to mock
the methods of interest.
But the benefit of doing it, is that you have some real data
that you can then use in your higher level.
I have some real data here that I've returned from get All.
So in my high level code that I'm testing,
when I say get All,
I've got some real data with real numbers
that I can test my algorithm on to see if it works.
So sometimes it's too much effort
to define these mock classes.
But I think in this case, it's actually proved it's worth.