[21/38] - Don't Mock What You Don't Own
2K views
Feb 14, 2023
Click here for the Code / Write Up / Full Video Series - https://www.codereviewvideos.com/course/let-s-build-a-wallpaper-website-in-symfony-3/video/don-t-mock-what-you-don-t-own In this video we will exploring mocking in PhpSpec, and how if we don't keep control of our code we may end up having a really tough time with testing.
View Video Transcript
0:00
Hi, I'm Chris from Cod ReviewVidios.com, and in this video, we're going to get started
0:08
implementing our tested approach to file upload. In the previous two videos, we've implemented an untested approach to handling file uploads
0:16
inside our easy admin bundle back end. And whilst this works, we've tied ourselves very heavily to the way that Symphony works
0:23
in particular the way that Symphony's file upload process works. And on the surface of it, this doesn't seem like that big
0:29
of an issue, but when it comes to writing our tests, it does kind of throw a spanner in the works
0:35
What we're about to do is to revert back to the way our code was structured at the end of
0:40
video 18. To do this, I'm going to check out the code as it was when we tagged our code at the
0:44
end of video 18. So I'm going to use Glog, which in my case is a shortcut for that command
0:50
So I say Glog, and I can see here a list of all the commits in this project
0:54
And the one that I'm interested in is the CF521. So press Q there, and then I'll do a Git checkout
1:00
that commit hash and so we've now checked out that code but to start working on it properly
1:04
what I'm going to do is as it says git checkout dash B and then a new branch name so I've got a
1:09
short cut for that and I'll just call this vid 21 now as part of the previous two videos we made a
1:14
number of changes to our database so rather than trying to revert them manually or anything like that
1:19
I'm just going to drop the database then recreate it after which I'm going to apply all the
1:23
migrations and then I can repopulate the database using our fixtures. One
1:29
Something that I'd like to do before I reload my fixtures is just make that change that we saw during our prototype phase, where if we look inside our wallpaper entity for the file property, we're not going to want that column annotation
1:43
So I'm just going to get rid of that. And because I've made a slight change to our schema as a result, I've removed a column
1:48
I'm going to come back into here, just press the up arrow once there to get our previous command back and do a diff
1:54
Now just make sure that we've got that drop file column as part of our migrations
1:58
Control L on a Mac there to clear the screen and I need to apply that migration
2:03
So again, just up arrow there and we'll apply the migrations. And with that change in place
2:08
we should now be able to reload our fixtures and our database should be back to its expected state
2:16
So with all of that setup out of the way, we can get back on with testing our implementation So we already have a spec for our event listener wallpaper upload listener So what I going to do is I just going to open that by splitting it vertically closing that one down having that one open as well and closing down the sidebar
2:33
And just to make sure that we all know where we're at, I'm going to do PHP vendor
2:38
bin, PHP, spec, run, and then spec, at bundle, event, and because it's the only one
2:44
I can just hit tab a few times there, and we can see we're pretty much where we're at
2:49
at the end of video 18. As you've gathered by now, it's not true TDD
2:53
which we're practicing here. So what I'm going to do is I'm going to refer back to our reference implementation here
2:59
So inside the repositories on GitHub, we've got the wallpaper website tutorial. What we need to do is get to video 20s commit
3:05
which is the most recent one at the time of recording. And I'm just going to browse the files
3:10
as they were at this point, going to the source, app bundle, event listener, wallpaper, upload, listener, implementation
3:17
And this is what we were dealing with when we had a working file upload process
3:22
So in order to port this knowledge across to our test suite, let's just quickly step through what this does so far
3:29
So I'll start off with the first line, which is to store the outcome of event args get entity into a variable called entity
3:36
Now, Lifecycle Event args has this method get entity. We don't need to worry about how the entity will be set
3:42
at something that doctrine is going to do for us behind the scenes. But what we do know is that this pre-persist
3:47
method is going to be called whether this is a wallpaper entity or a category entity or any other
3:52
entity that we come up with in the life of our system. So what we need is this guard statement that
3:57
says if we're not dealing with a wallpaper, then just exit early so that we don't end up doing a
4:01
bunch of logic needlessly. If we get past that return statement, then our entity must be an
4:06
instance of wallpaper. We're adding in this type in just to make PHP Storm able to understand
4:11
what our entity is and therefore give us some nice auto-completion on our methods. One of the properties
4:17
on our wallpaper entity is the file, even though we're not storing that information
4:20
in the database directly. When dealing with an upload, the file will have been set
4:25
We know that it will be an instance of Sympani's uploaded file, so we can use the methods that are available
4:30
on that class or any of its base classes, which is the file and the SPL file info
4:35
as we need to. On the SPL file info, there's get path name
4:39
We can call that and we can figure out exactly where we're at currently. We created a new service
4:44
which would allow us to take the uploaded file file name and then concatenate it with a path that we care about which in our cases our project directory then the web directory inside that and then the images directory inside that And that will become our new file location With those two pieces of information
5:00
we can move the file from wherever it is currently to the location that we wanted to be in the
5:05
longer term. And then we were getting a little bit fancy there to pull out the width and the
5:09
height dynamically, and finally setting some properties on our entity. So the most important part of this
5:14
for us is going to be getting access to the file. How we get access to that file is when we go into Easy Admin bundle into our wallpapers
5:21
and we do our Add wallpaper. We've set up a form in our Easy Admin bundle configuration
5:28
So we're going to App, Config, Config, Config, Easy Admin bundle. And we've got this form here which tells Easy Admin bundle how to create a wallpaper
5:37
or what field we would need to create a wallpaper. And the important one here for us is this property file of 10
5:44
type file. And what that's going to do is it's going to add one of symphony's file type form
5:49
field types to the outputted form, as you can see here. Now because this is a symphony form
5:56
the way in which the implementation behind the scenes works will be the same way in which a
6:00
symphony file upload always works, which is how we get to having a symphony uploaded file
6:05
instance as part of entity get file. So knowing this, let's put this information into our
6:12
tests. It's going to close down some of this to make it a little bit tidier. And so inside
6:17
it can pre-persist. Let's start adding in some of this new information. So we know we're going
6:22
to get access to some form of uploaded file instance. So let's add that in as something that
6:27
we're trying to mock. So I say uploaded file. Let's tap that in there as well. Uploaded file
6:34
Let's pop that in. And we'll say whenever uploaded file get path name is called, we will return
6:42
so we'll return our fake temp path. So again, looking at the code
6:47
we're going to get the temporary location when we call file get path name. So we've just faked that there
6:52
and said when that is called, we expect to always give the fake temp path
6:57
Okay, so next what we'll do is we'll put this uploaded file onto a wallpaper
7:02
And rather than mocking that, we'll just create a new one. There's nothing really that we need to mock here
7:06
that we couldn't use a real instance for. So we'll say wallpaper, set file
7:11
and we'll put in that uploaded. file And again looking at our code we can see that we going to call event tags get entity And so when we call get entity on event tags which we currently mocking what we going to do making sure to do this after wallpaper has been set up we say eventires get entity will return our wallpaper
7:31
So all things being well, even though we've not got this file mover move set up at the moment, the pre-persist part of this test should kind of work maybe
7:40
Well, let's give it a shot. So we get an interesting exception here, which is to say that the file was not found and the file speech marks does not exist
7:48
In other words, this empty path or maybe this empty file name, it doesn't exist, which we kind of know it doesn't because we're trying to fake it
7:56
But this kind of leaves us with a problem. If we can't fake it or if we can't mock this file, then what we're going to do
8:03
So the next thing that we might try and do is to rather than use this mocked version, we'll get rid of that, and we'll just create a new instance of it
8:10
So I'll say uploaded file equals new uploaded file. And we can see this takes a couple of arguments which are not optional
8:17
the path and the original name. Well, we have a fake temp path, so let's just pass that in
8:22
And we can give it any name that we like, I guess, some file, as that's the name of the file that we kind of fake him
8:28
Because we're not mocking this, we don't need to tell it what to return because we're passing in the temp path anyway
8:33
When path name is called, that fake temp path should be returned
8:38
At least that's the theory. So let's give that a shot. Now we've got a slightly different error
8:43
The file temp some file does not exist. And indeed it doesn't because we've just tried to fake it
8:49
And that file doesn't actually exist in this location. So PHP can't find a file, as you would expect
8:55
And at this point we seem to be in a really bad position. We can't mock the uploaded file and we can't use a real instance
9:02
So how on earth do we proceed? Well, all of this is a result of us losing control of the way our system is working
9:09
Rather than making symphony bend to our will, we're bending to symphony's will
9:14
Now there's like a golden rule with PHP spec, which you will see banded around
9:18
which is to say, don't mock what you don't own. We neither own the life cycle event args, nor the uploaded file
9:27
So to finish up with this video, I want you to think about how we can start owning the concept predominantly of the uploaded file
9:35
or a file in general, and we will look at one way of solving this problem
9:39
in the very next video