Hi, my name is sandro mancuso
in this screen cast
we're gonna take a piece of existing code without test
we're gonna write tests for it first
and when it is 100% covered
then we're gonna refactor it to make it better
so, the business requirements are
imagine a social networking website for travellers
so, you need to be logged in to see any type of content
and as soon as you logged in
if you want to see someone else's trips
you need to be friends with this person
it's kind of like your facebook stuff
so, if you're friends with someone you can see this person's trip
otherwise, you can't
so, but there are a few rules
we can not touch production code if it's not covered by tests
so, as we know, sometimes we should write test for existing code
we need to change the existing code
so the only exception to this rule is that you can use automatic refactoring
by our IDE, never type into the file
so, one approach that we gonna take is
imagine that this is how your code looks like
instead of trying to go to write test from the deepest branch
we're gonna choose the shortest branch of the code and write a test for that
and then we look at the second shortest branch
and then write a test for that
cause that gives us the ability to test by test
understand what our code does
and also we build up our test suite so we can get to the deepest branch
if we start to write a test according to the deepest branch
or trying to test the deepest branch
normally we need to create a massive setup to get there
so, we need to understand entire code base
so we prefer to do from the shortest branch to the deepest
let's see some code
so this is the trip service class
this is a server side class
and basically what it does is
it has a get trips by user
receive a user in there
so it checks the user session
gets the user that is logged in
then if the user is not logged in it throws an exception
if the user is logged in then
check if the logged in user is friends with the user passed as parameter
and if they're friends then I can retrieve
the list of trips for the user passed through parameter
so a bit confusing for now but what I'm gonna do
let's start writing test for it
may I just split the screen
that's normally how I prefer to code
I normally split my screen into two
I have my production code in one side
my test on the other side
then I don't need to keep switching from one to another
so that's my favorite way of coding
so, the first thing that we do
we just look for the shortest branch
we can ignore entire thing
I don't care what this does for now
for me, when I'm working with legacy code
the first thing that I do is
if I want to see what this code does
I look for the shortest branch
if the logged user is different from null
then it throws an exception
so if it is not logged in throws an exception
so let's write a test to see if that's true
so, should throw an exception when user is not logged in
let's put the exception here
expect it, user not logged in exception
so let's create the trip service
import that
now what we should do
trip service dot
get trips by user
pass as null here for now
I'm using Infinitest
so this red bar here basically show
everytime that I save my test or my production code
it runs my test for me
so I don't need to keep trying with my hands
this should be in theory
green
but something is happening
so if I run junit by hand
basically what's happening is
this should throw user not loggedn in exception
it's expecting user not logged in exception
but it's throwing a dependent a class during blah blah
so basically what's happening is
when this line here is executed
so when I say user session, get instance, get logged user
the user session
I did that on purpose of course
throws an exception
basically because in a unit test
we shouldn't be invoking other classes
because this may have the dependency of http session
it could go to the database to retrieve stuff
so we don't want that
so we need to break these dependencies
the problem that we have is
this is a singleton
and I cannot mock it right now
so I can't inject it, I can't type inside this trip service
but I need to find a way to get rid of it
I need to find a way to mock that
so the only things that I can do, as I said before
is I need to use automated refactoring
but I cannot not type
so here what I'm gonna do
I'm gonna create a seam
so seam is the division of where the two classes
the seam is where two classes meet
the trip service and the user session
so I'm gonna create this seam
so what I'm gonna do, I'm gonna create a method
saying get logged in user
and I will make that protected
so I just isolated the bit that goes to the other class
and that's my seam
so now what I can do as an intermediary step
I can create a private class, testable trip service
that extends the trip service
so what I do
I override the get logged in user
and I make it return null
and in my test, I replace the trip service by testable trip service
and so if I try to run that
now I have it green as you can see here
my infinitest somtimes goes crazy
sometimes I can trust, sometimes I cannot trust
in this case it was red but my tests are green now
so in theory I should be happy with that
just to confirm, so my test is green
and if I run my code coverage
so I make sure that I'm testing the shortest branch
so that's another good tip
cause every time that I work with legacy code
I always use code coverage
so lots people thing code coverage ...
ah... it's test coverage
I don't care about the percentage
the important thing for me here is, when I'm working with legacies
is my test covering the branch that I thought it would cover
because this avoid the false positives and negatives
so I know exactly what I'm doing now
so that's cool
so let's remove that the code ceverage
however this test is far from being good
because look at this
should throw an exception when user is not logged in
this test is all about the user not being logged in
but where does it say it in here
here we created the testable trip service
we invoke a method, pass null, throws an exception
but where's the user not logged in?
it's here, it's hidden
so we need to fix that
we need to introduce the concept
oh by the way, the reason that I have this weird notation in here
it's kind of weird for java people
but normally the Ruby they use this convention
because I can just collapse the method
and then I have the specification of my class
and normally I read like
trip service should do something
so that's how I normally prefer to create my test
so this is all about the logged in user that is not there anywhere
so I'm gonna create it
logged in user is null
so now I'm gonna create this guy
create field user logged in user
and I'm gonna say logged in user
so now it is little bit better because I introduced the concept that I was talking about
I don't like the nulls
cause null doesn't mean much in this case
so let's say logged in user is not logged in
or in fact in a web application
the opposite of logged in user is normally a guest
so here we take the opportunity start using the concepts that
you probably gonna speak the behavior to business people
right, so it started to use your domain language
in here, I have the user that should be friends with or not
in this case, I prefer to make it explicit that is
unused user
so now, see if I can
I'm just trying to see if I can make my Infinitest be happy
but it's not
so I won't be trusting my Infinitest unfortunately
so I much happier with my test now
and by the way my Infinitest is working again
so now my test reflex what I was talking about
so should throw an exception when user is not logged in
so the logged in user is a guest
and then throws an exception
so awesome
so what we can do right now is
rerun the code coverage
I just want to run the junit first
just to make it sure
cool
now I can run my code coverage
so now what I do is
according to my code coverage
I can easily see what is the second shortest branch
so basically, now is this for loop in here
and then I say if it's friend
so this is the deepest
cause to get to here this flag needs to be true
so basically what I need now is when the user is logged in
but he's not friends with the user passed as parameter
so we're gonna write the test for that
so let's keep the code coverage on
so let's say
should not return any trips when users are not friends, right?
so I'm gonna borrow these guys here for seconds
so now I need a logged in user
so I say, a registered user
let's create this guy
and here what I would need is a list trip trips
let's call it friend trips
so import all that
so basically what I need now
I need a proper user
I can't have a user in here
so I'll call it a
let's call it friend
so I need a user that is friend
so add friend, another user
friend, let's say add trip to brazil
so let's create these guys
ans so now I have a friend
that is friend with another user that has a trip to brazil
I have a logged in user that is a valid user
but they're not friends
so I need to assert that
friend trip dot size is zero
right, I could have said
assert that is empty doesn't matter
so my Infinitest that apparently is working
just confirm running the JUnit command
so it saying that it is green
that happens quite often in legacy code
cause the code is already there
you just making sure that you understand what code does
so that's quite common
I'm just run the code coverage
so running the code coverage now we progressed little bit
so as you can see my new test now moved forward
so went for the shortest branch
where the user was logged in
but they were not friends
so it was returning always false and then that it
so we're not done yet
because now we need to do some refactoring here
there is some duplication
so let's get rid of it
so let's put a before method
let's transform this guy
local variable into field
it's great
it's awsome
now we can move this guy up
right
so now I can go to the deepest branch
so now what I need is
when the logged in user is friends with the user passed as parameter
then I need the trips, return the trips
should return friend trips when users are friends
right? so that's the one we testing
so I'm gonna borrow this again
so you shouldn't do copy and pate
I don't do copy and paste
what you saw was an illusion
so I never, never ever do copy and paste
so in this case here what I want you to do now is
friend, add friend with logged in user
let's add another trip
so now of course that I expect my Infinitest are red saying with this
red, of course I expect this should be true
because now the users are friends
and if I save
it still saying that it's red
so let's run junit by hand
oops, I keep getting the shortcuts wrong
because I kept using windows
my mac and intellij keep getting (???)
so what it's saying is, because now what's happening
just restore this screen
what's happening is
the code is now getting here in the trip dao
that is a statical
and as soon as the code execute this guy
then there's an exception being thrown
now I give the same thing
so this is just to prove the point that
just to make a point
that we shouldn't ever be calling or execute another class in a unit test
because this, in the real world, would be going to the database and fetching data blah blah blah
the test for my trip service shouldn't care about that
so we need to do exactly the same thing
that we've done for user session
so you can break the hard wired dependencies
like singleton and staticals
or objects being created inside method is exactly the same
so basically what we do
create a method
let's say trips by user
that is also protected
and then we go back to our tests
we'll do the same thing
so basically what we'll do
we will just return
so we will receive the user
the user in our test that has two trips
so basically we just return that
the user dot trips
so as soon as do that my test is green
so if I run my code coverage
awesome
cause now I have all my existing code base
tested to almost 100%
of course that you might say that I created are not test because I overriding them
but that's alright because that's just my seam
that's a very thin layer to delegate
and that's intermediatary stage anyway
so now few clean ups that we can do
normally what I do is
because this guy is here and is here
and the only situation that I don't want it should be like that is this one
so I leave this one
and I just move this one out
because then it is common to all other tests
all other tests that I want the logged in user should be a valid one
so once I do that
if I save
then if I just make it sure
so all my tests are green
and that's great
so for the first part, it's great we're done
now our code is tested and we're free to start refactoring
so now that we're in a very good point to commit
so git status
git commit
unit tests for trip service
cool, awesome
look at this test, the problem is
legacy code is that quite often you need
this is of course very simple one
but imagine like a same method with hundreds of line
and here we're just talking about one object being created
to pass in as parameter
but quite often what we do is we need to create this massive object graph
to pass in as parameter
so we can reach all the branches and stuff
so we can start cleaning that up
cause I don't like all these duplication in here and here
so the way that I would love to see it
is using a builder
so basically what I loved to see is something like that
user friend
kind of like a friend or a user
friends with another user and logged in user
with trips to brazil and to london
so that's the sort of stuff that I would like to see
I think I read far better
so let's try to see if we can make that happen
so let's put a build method in here
let's use the builder pattern for that
let's say user builder
so that would be what I want to see
so I will gonna comment this out a little bit
so I'll say, public, static
class, user builder
let's start creating this method in there
the way that you can chain methods
you'll just keep returning the same object
and then you can chain all the methods
so let's create the use builder
so now we just create the next method
keep returning the user builder
so here we're passing two users
as I always said, there are just three numbers in programming
there is 0, there is 1 and there is many
there's nothing more than that
so let's make it many
so let's use variable arguments
cause that I can pass many users as I want
so let's put friends
let's say these friends equals friends
let's keep returning same objects
let's create this guy
should never leave arrays uninitialized
so new user
avoid some null pointer exception
so great, let's go for our second method
so user builder
let's do the same thing
because then we're free to use our method to pass as many trips as we want
let's return trips
let's create trips
normally what I do
I write the way I want to read it
and then I start creating all the code from there
and let's create the build method
so build method, I like it to be the last method
so the build method is the method that actually build the object
so new user
let's return the user
and then I say
add trips to user
add friends to user
it's always to cool to combine the method name and the parameter
it makes it much easier to read it
avoid duplication in names as well
so let's do the trips first
so for each
here we're doing the trips
so that's a trip that's a trip that's trips
so user dot add trip, trip
let's do the same thing for friends
create add friends
let's do
for each
so that's user
that's friend
friends
user dot add friend friend
awesome, cool
so now, Infinitest saying everything is green
so I can delete this
and I can replace this one as well
so let's replace this one
awsome, let's comment that out
so if I run everything, still green
again, awesome
so now my tests are slightly more readable
so what I will do move this builder out
so let's say
move type to new file
and basically what if you do
if you just move this class the same folder where the TripServiceTest is
for now it's good
so now that is out
static import this methods in here
so let's make code symmetric
so both of them are symmetric
and let's remove all the unutilized import
so there's no unused imports anywhere
so excellent
so now I'm much much happier with my test
because then I can read it properly
and that's a great point to commit as well
so let's see
we got status
git add
git commit
user builder created and used in trip service test
cool, awesome
so now we're finally ready to start refactoring this
as we're saying before
where was it, here
we normally start testing from the shortest branch to the deepest
and when we're doing refactoring it's the other way around
you start from the deepest branch
to the shortest delta branches
the reason beneath is
if you want to start refactoring in the middle of your code
you need to understand everything
and normally
legacy code, they have global variable everywhere
and dependencies
set a flag in one place, checks it on the other
but if you go straight to the deepest branch
the deepest branch doesn't depend on anything
it normally receives everything that it needs that is needs
and then it does something with it
if you're able to extract that
refactor that the deepest branch from deepest to shortest
you're code is start shrinking
so let's look at the code
so the deepest branch of the code
as we discussed before is this one
so to get to here
I need this flag to be set
the flag is set here
the problem in this example in specific
the deepest branch that is this line
doesn't do much
it just delegates to this one
so I can re-do anything with one line
so the second deepest branch is this one
right it is this bit in here
so let's go for this one
one thing that is very very important
when working with legacy code
is to understand why the method is so big
why is it so complicated?
and normally it is because the method, of course, does too much
that's why it has so many lines
so it's important that instead of us trying to make the algorithm better
or extract method in here
we should ask ourselves
does this responsibility
does this behavior belong here?
in this case, look at this
if you look at this block
so we go to the user object and asks for the friends
so we get a list of friends out of the user object
we iterate through it
just to ask if the logged user is inside that collection or not
so this is called feature envy
it's kind of the trip serivce envies the user
it wants to be the user class
so what we could do here, we need to solve the feature envy
if the user class has the collection of friends
we should ask the user class, like
user are you friends with this guy?
so, let's try to move this behavior there
so the first thing is
let's look at the user class
where's my user?
so let's close this too
so that's my user class
let's create a test for it
so my user class is here
new class, let's say user test
but I'm gonna put that in the test package of course
so that will create my user test in here
let's move my test here
cool, so what do I want?
I want this behavior here should be in this class
I want to go to the user and say,
"Are you friends with another user?"
right?
so, I'm gonna write this test
should inform when users are not friends
I'm gonna start with not friends
because it is the easiest test that I can write
this is the simplest one
here I can use my builder
user builder dot a user
so let's say that this guy is friends with Bob
right? let's do that
and then in my test, I'm gonna say
assert that user is friends with Paul is false
so basically I want to create a user that is friend with Bob
and of course if I ask this user if he's friend with Paul
it should be false
so let's create Bob and Paul
let's create Paul as well
so now that's the method that I want the user class have
let's create it
and of course the minimum behavior that I can have to satisfy the test
is false
so as soon as I save the false
my Infinitest tells me that it's passing
that's awesome, great
so let's do the other case, when the users are friends
it should inform when users are friends
so I'm gonna borrow these guys here
let's move that down
and what I'm gonna do is
you're also friend with Paul
and I expect it, of course
it should be true
so my Infinitest are saying that it's not true
now I'm gonna just implement
just friends dot contain
Paul doesn't work here
let's say another user
cool, so now I have my method in there
awsome so of course that is covered
there are ones that are not used in this one
that's alright
so this is a great point to commit
git status
git add
so git commit
is friends with method added to user
awesome
so now we can go back to our test
just remove all the recorded code coverage marks
now I have the user with the behavior that I want
so when we start to refactoring
we should try to stay in the green
for as long as possible
instead of going crazy in the refactoring
and say I'm gonna change everything
and then all the tests are broken
we should, every single step that we make
we should run our tests
using Infinitest, is a way works well
is fantastic because that
if you make one move save it and it runs your test
and then you know where you are
and as soon as you make one move
something is broken
so your test shows the red in there
you're one Ctrl+Z away to be back to green
you just Ctrl+Z and save it
you're back into green
so let's try to make the smallest step that we can
to do this refactoring and stay in the green, right?
so what if I say
what if I first of all move this boolean in here
because it just used inside of the if statement so I can bring
oh, there's another tip
when working with legacy code
normally you find variables declared all over the place
try to bring them together
try to put the blocks of code close each other
if the variable is not used at the top
it's just used at bottom
but it's declared at the top
bring that close
because it ??? know where the blocks are
and it makes you much easier to separate the code
so now I could say
is friend, I could say
user is friends with logged user
let's rename logged user to logged in user
so that's how I think
when you're dealing with legacy
you find names are quite weird or not quite right
as soon as you figured out where they are
just rename it quickly
so cool, as you can see
you can keep an eye on the green bar here
so every time that I saved it
it's gonna run my test
so now, I believe that is quite safe
to do this and save
so it ran my tests and that's green
so now I can delete that
so now that I can delete
I maybe able to
let's try to inline this
awesome
so that's cool
so there is a quick win as well here
for example, this guy here
it's a guard clause
we can transform this into guard clause
what is a guard clause?
as soon as parameter comes in
you invalidate parameter and throw an exception
that's the guard clause
so what we can do
let's transform that to guard clause
we can get rid of if
so if logged in user is null
then
let's throw an exception
of course that this guy need to be up
so as soon as I saved it
so I'm still in the green
and Eclipse kindly showing to me that this is dead code now
so I can delete the entire if statement
so if I save it
awesome
so I'm doing all these refactoring without going to the red
not even a single time
but even if I make a mistake and I go to the red
what normally happens in the real world
that's alright, I do a Ctrl+Z
I'm back into green
and I re-think what I'm doing
sometimes you need to go to the red
but I avoid it as much as I can
so now we have two clear blocks in here
the first block is about checking or validating the logged in user
the second block is about getting the trips from the friend
so we can do a few things slightly better in here
so for example I could say
getting rid of variables in legacy code
is another thing that you should try to do
because the less variables you have the easier it is
even if you have some redundancy in there
if you call method twice
you may sacrifice performance for a while
but getting rid of variable
cause variables are the most people ??? in long method
because they're set in many different places and reused
if you can get rid of them
and just invoke the same method over and over again
it'll be easier to read all the code
and then you do the optimization for ??? again, if you need to
so for example, what I'm trying to do here is
so saved it
I'm still in the red
so I believe that what I could even do is say
return
so I'm still in the green
so return
of course it don't need
so if I save that I'm still in the green
and I don't need the variable
don't need that
still in the green
and now that I have symmetrical
if I can use ternary operator
so I could do that for exmple
hopefully if I save that
I'm still in the green
awesome
another thing is
this variable here is used in two different places
so let's inline that
inline from here. yes, I can
it's inlined in both places
that's what I was talking about
I got rid of the variable
and of course I'm calling the method twice
but I made my code a lot simpler
of course if there's performance issue
then you extract variables again all sort of stuff
so that's awesome because now I'm much happier
and one thing that I don't even like this guy here
let's extract method in here, and say
no trips, let's make it private
so the cool thing about it is
that for example
if we look at the code
if I look at my test now
should throw an exception when user is not logged in
get logged in user
throw an exception
should not return any trips when users are not friends
if user is friends with logged in user
no trips
should return friend's trips when users are friends
so if user is friend with logged in user
trips by user
so you end up saying the same language used in the test or test names
being reflected in your code
how your code is written
that's one of the things that I want you to achieve
so that's another good place to stop and commit
so let's do that
so git status
git commit
trip service refactored
cool
so that's awesome
so we may think that this is good, right?
we are done
but NO.
we are far from being done
see, there's tiny piece of code
but for example, we have enormous problems
one of the problem is
what we've done here is
we took a piece of untested legacy code
and then we wrote test straight
and when we do that
that's great, that's awesome
cause then we feel confident that we can change it
(???)
but the drawback is that
what if the design is wrong?
cause if the design is wrong
what we're doing when we write test
we're perpetuating that design
so we need to be very careful
because if we're not very inclined to change the design
and now we have a battery of test covering that stuff
we're far less inclined now(???)
because as soon as we start to change design
you'll break all those tests
so you need to be careful with that
and why the design here is wrong?
well, by the name
I assume the trip service is a server side class
belongs to the Model
but then it has a dependency on the user session, right?
so your Model shouldn't know anything about the web framework
shouldn't know anything about http session or anything like that
so, that's the problem with the design that this class has
so we need to solve that
one way to solve that is
we should pass the logged in user as parameter to this method
so now I'm gonna make sort of dangerous refactoring
that it will be ok for this exercise
but if you do that in a normal production environment
you need to be very careful
so I want to pass
I'll try to do, as always
baby steps, bit by bit
so for example I want to pass the logged in user as parameter
so what I can do is
I go to refactor
change method signature
and I'm gonna add a new parameter
I'm gonna call user
logged in user, right?
and the default is null
so that's cool
because eclipse does great job in refactoring
IntelliJ does same
so this guy was passed in as parameter
it's not used anywhere else
but it changed in my test
it changed the method signature to pass null, everywhere, right?
so that's cool, that's great
the only problem is, for example
this method here being a production method
it's probably used
or it's probably invoked by other classes
and now all the other places where this method here is invoked
the production code is passing null as well
so you need to go back there and fix that
so you just need to bear that in mind
but for the sake of this exercise
so what we want to do
we want to do fix our test
cause we don't want this nulls hanging in around
and this is the logged user
so I'm gonna pass here, a guest
because that's what I would pass
so as always I'm looking at my Infinitest
so here I'm gonna pass my logged in user
here I'm gonna pass
yeah, I'm gonna pass my logged in user here
so that's cool
just to make sure that everything is ok
so cool
now I want to start using this parameter
so what I'll do
I'll just use the parameter
I'm just gonna rename that to this
rename that to this
so this is the parameter now
so if i save it
and so all my tests are green
so that means that this guy
transform that in private
so eclipse say that it's not been used
so I can delete safely
and of course, as soon as I delete my test
I just need to delete this guy
and everything is back to green
so now I'm using the parameter
I made my refactoring
(???) thing that I can do is
this logged in user
I probably don't need that anymore
so if I replace this one by these guys here
and I put this guy here
so one way to know if
so now for example, eclipse is saying that this variable is not being used
so now I can delete that
and delete that
delete that
and everything is green
so this is a perfect place to commit again
oh, by the way, there is a warning here
so should always get rid of the unused import
because rubbish thing around (???)
so craftsman ship is important
keep it clean
so cool, that's a great place to stop
so let's say
git commit
logged in user passed into trip service
cool, awesome
so maybe now we're done
sort of, sort of
I still don't like this thing in here
this trip dao thing
I don't like the statical
this statical makes me do this thing that
as I said this is sort of intermediately step
ideally I would inject trip dao in there
using an instance method
but because it's static method, I can't do much
I can't really inject that, right?
cause this is always
you mean normally inject an instance not class
so it doesn't work
so I want to go further
I want go get rid of this
I want to have a instance method in my trip dao
so let's open my trip dao
let's see what it does
so of course that in this case
it is just like a toy example
so you just throw an exception
as I said before in a normal circumstance
this would go to database
or to distributed cache
or whatever persistence mechanism you have
so what I want to do
I want to create an instance method for that
so I'm gonna write a test for it
let's say
trip dao test
let's put into test
so this is an interesting one
it's an interesting trick
so I want an instance method
that does everything that the static method does
same behavior, just
you could say, well, just remove static method
just remove the static keyword from there
yes, I could
but if I do that
I'll need to fix my entire code base
because, let's assume that there are many classes in my application
that are using this static method
as soon as I transform this one into instance
I'll break entire code base
and that'll be much bigger refactoring to do
and we want to do everything in small steps
so what I'll do
I create a test, let's say
I'll just mimic this behavior
so don't take this test too seriously
just get the idea of what I'm doing
but I'll just represent what this method currently does
so should throw exception when retrieving user trips
so I want this to throw an exception
as I said
ideally this would go to the database or something
I'm just preserving the same behavior
so dependent class call during unit test blah blah
so that's the exception thrown
but I want an instance method
so just invoking the method should be enough
so what I can do
new trip dao
let's call it trips by user
so I want an instance method
that throws an exception
so I'm gonna create this method
I'm gonna make it return the same thing as the other one
so my test is failing, of course
so now what I want this to do
it should behave exactly like this static method
so what I do is trip dao dot
I just invoke from the instance method
invoke the static one
why do I do that?
the cool thing is that
as I start moving my code to use this one
so now what I'm gonna do
I'm gonna replace the trip service used the instance method
and imagine that I start doing that
for all the other class that used static
until there's no class referencing the static method anymore
and what I can do
I just can copy this behavior
paste it into here and delete the static method
and then I'm done
so that's the trick
so going back to
so now my trip dao has the instance method
so what I need to do now
is to have a reason to inject this trip dao in there
so now I'm gonna
there are quite of few ways of doing that like for example
I could use normal dependency injection with no framework
just pass the trip dao into the constructor of the trip service
for example, that's one way of doing it
and that's the simplest way
as I know that majority of people that use java
they use spring and they use mockito and stuff like that
I'll show you how it would work in mockito
but for now, if you don't use mockito
then that's it
create a constructor pass the trip dao to the constructor
I'm just gonna do with mockito and spring
just to show what you can do basically
so I want now to have a broken test
that justifies me to inject a trip dao in there
so, I'm gonna say run with
I'm gonna use JUnit with mockito
mockito runner
junit runner
so if you don't use mockito and the junit in spring (???)
maybe a bit complicated
so I recommend that you read the documentation of all these frameworks
so far, I've been using the testable trip service
that class we created before
now I want to start using the real one
the real one and injecting
cause I don't want these thing here anymore
I don't want this madness here anymore
I want to just to be able mock to my dependency
so I'll create another
do that in very small steps
trip service
I'll call for now real trip service new trip service
that's the real one
I'm gonna create a mock
I'm gonna create a mock to my dao
I'm gonna use mockito, to say
inject mocks
and I'm gonna spy on it
so basically it's out of scope for me to explain how mockito works
but basically in very short explanation
I'm creating the real class
I'm asking mockito to spy on the real class
and to inject all the mocks that this class may have
I create the mock in here
I just need to inform that this class is expecting this mock
so but before I go there
I'm gonna, now I'm using the real trip service in here
I'm gonna where it breaks
so for example
I can go bit by bit, I can say
real trip service and save
so my test still green
so because what I'm trying to do
I'm trying to get rid of this guy now
bit by bit
to replace with a real one
so real trip service
so if I do that
it works, cool
the reason it works
because none of those test that use, they go the dao
none of them get to this line
so this one, real trip service
so now it breaks, right?
that's what I was looking for
now it breaks because
I'm using in the test the real trip service
and that calls the static method in the trip dao
and that throws an exception
so cool, now I have a broken test
so I have a reason to change my production code
so what I'll do
I'll go to my production code and say
if I'm using spring
auto wired
so just import the auto wired
so imagine that if you gonna define the trip dao in a spring bean
in a xml
so private trip dao trip dao
awesome
maximize that
so cool
so what mockito will do
mockito will take this guy here
mymock and inject into this guy here
so now I need to start using that
so my test is broken
and I need to start using that
so in here, instead of using the
so if I run, I just run a test
just to show you the difference
run as
I keep pressing wrong button
so it's due to using the real dao, right?
so what we're gonna do is replace with the instance
and say use the instance method
instead of this classic one
so now if I run my test again
the error changed
so basically it's now using my mock
so if I just minimize it again
so now it's using my mock
and it says that the test expects two trips but it got zero
so that's what my test does
my test's expecting two
so what I need to do is
the reason that this is happening
because now my production code is using the mock
and the mock is not configured
so I just need to say that
given my trip dao
dot trips by user
so given that this method is invoked in my mock
it will return friend dot trips
so what I'm doing here
I'm configuring my mock to return the friend trips
exactly as the way we've done here
I'm doing in here just configuring my mock
so now as you can see my test is green
so this guy can go back to private now
and I don't need this method anymore
and if I run all my tests
my tests are all green
so that means I don't need this class anymore
so that means that I don't need this
and if I don't need this, I don't need this
and in this case, this guy is not been used anymore
I can delete
so now I can remove
I can rename my real trip service
because real means nothing
so when we're doing test
I've seen people calling variable like testee or mock (???)
call it what it is
it's a trip service. so it should be called trip service, right?
so now it's great because
I don't have that madness of test double class anymore
my test reads quite well
and so just make sure that everything is green
I can even go there and say
so validate logged in user
so now as I said
I can close all my test methods
and we can read this block here and say
should throw an exception when user is not logged in
that's part of the validation of the logged in user
should not return any trips when users are not friends
so if the users are not friends returns no trips
so should return friend trips
when users are friend
so if the user is friends with the logged in user
just return the trips
even this getter method here is not great
so get trips by user so that could say
get friend trips
that sounds much better
this guy could be
friend as well
or something like that
so you can play with these things now
that you have all the code coverage
so basically that's another great point to stop
so git status
git add .
so git commit
trip dao injected into trip service
so this it it
that's normally
that's what all the things that you can do
with legacy code in a very safe way
I can show you
I have original trip service somewhere in here
so you can compare
so we went from this to this
and of course that
it took me just an hour to do that
but it's because I'm recording this screen cast
but as soon as you're comfortable with all these techniques
this probably would have taken me in a normal day like 20 minutes
so it's just like
as soon as you're aware of what is available to you
then you just practice it
and then it makes things much easier
and just as a reminder
every time that you're working with legacy code
your first test should never be the one that goes to the deepest branch
always start from shortest branch
from the shortest to the deepeset
write your test in this order from the shortest to the deepest
it's easier because the test will help you to understand what the code does
and it will help you to build the test data
bit by bit as you dive deep into the code
when you're refactoring
do the other way around
find the deepest branch
and start extracting method from there
starting put in that behavior into different classes
and then all of sudden your code will start
collapsing and getting smaller and smaller
because you're removing the internal parts
so try to write readable and maintainable code
try to have the domain language in there
try to capture that
like using things like guess(???) and stuff like that
so as part of the domain
try to bring the domain language into your test and into your code
the language spoken in the test and the code and used it with your BA should be the same
try to have simplicity
know your shortcuts, frameworks like
try to be fast
configure your environment so you can with a few shortcuts
create test methods and classes and all sort of stuff
and do the refactorings
and as I said working in very small increments
commit often
I mean if you're using git and stuff like that
because if you make mistake
Ctrl+Z or git reset hard
you go back to previous commit
and you're back into the green
be brave
now you know a lot of things like
all these small tricks that we learned today
we'll make probably 99% of all your classes testable
so just be brave
use it, and
always leave the campground cleaner than you found it
so that's the boy scout rule
no broken windows
if it's smelly
if it's not very well written
just refactor it
just go for it
yep, so I hope you enjoyed it
that's it
so you can find this trip service kata in my github
so just go to github sandromancuso trip service kata
that's it, hope you enjoyed it
thank you