< Return to Video

Testing and Refactoring Legacy Code

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

In this video, we take a piece of crappy Java code with no tests. Our objective is to write tests for it and then refactor to make it better. The code has the most common problems that much larger legacy applications have, like Singletons, static calls and feature envy. It also has some design problems. Fixing that is quite hard, mainly when we need to write all the tests before we start the refactoring. Another rule: We cannot change production code if it is not covered by tests but quite often we need to change the production code in order to be able to test it. How to solve this problem? Well, I hope I can answer all these questions in this video.

If you want to know more about Software Craftsmanship, please check my book: https://leanpub.com/socra

If you want to play with the code, clone it from: https://github.com/sandromancuso/trip-service-kata

more » « less
Video Language:
English
Duration:
01:17:08

English subtitles

Revisions