Lesson 14 of 21

Extract And Override Refactoring

Duration: 13:35 Watch on YouTube (external link, opens in new tab)

Markdown version

In this talk we learn about the Extract and Override Refactoring (external link, opens in new tab), and why it is one of the coolest things you should know.

In this lesson

Roy Osherove separates constrained isolation frameworks (external link, opens in new tab), which cannot override static or private methods, from unconstrained ones such as JMockit (external link, opens in new tab) and PowerMock, which can fake (external link, opens in new tab) them. Without them, he applies extract and override (external link, opens in new tab) to a string calculator (external link, opens in new tab) that calls a static logger: the call moves into a protected call logger method, which a testable subclass (external link, opens in new tab) in the test overrides to save the text in a public field. He also explains seams (external link, opens in new tab), keeping logic out of the extracted method, and starting legacy code (external link, opens in new tab) with integration tests before refactoring (external link, opens in new tab).

Transcript

2,051 words

[00:08] (external link, opens in new tab) >> Okay. >> Hello. So up until now we've dealt with standard things instance methods, virtual methods, things that we can override. But what happens when we we we come across things that we can't easily override? we can't replace the instance of things because maybe a class doesn't receive an interface. Maybe a class uses static methods of a logger. Well, this is where we start looking at other ways to test our code.

[00:44] (external link, opens in new tab) Now, before we do this, I want to mention that there are two types of isolation frameworks (external link, opens in new tab). There are constrained and unconstrained isolation frameworks (external link, opens in new tab). Now, the constrained isolation frameworks (external link, opens in new tab) basically work like we've done so far. We create a class that inherits from something then we override the methods we care about. But also that means that those frameworks have the same constraints that we have as developers. We cannot override a static method for example. We cannot override a private method or a constructor

[01:17] (external link, opens in new tab) Unless we unless we have some kind of special tool. Otherwise we have to override to sorry to refactor things. So unconstrained frameworks allow us to basically test our code without changing it. Those include JMockit which is both a test framework but it can also run within JUnit (external link, opens in new tab) and it also includes the ability to fake static methods, private methods and other things and PowerMock which is an addition

[01:50] (external link, opens in new tab) An additional framework that you can use within EasyMock or Mockito and I'm going to explain about those and how you can use them but first I want to show how we don't have to use them if we don't have to. So I'm going to show a very common technique to refactor your code even if it uses static methods. And we're going to practice to use this technique. I'm calling it extract and override (external link, opens in new tab) so that we can learn how to refactor our code

[02:20] (external link, opens in new tab) From its dependencies. So extract and override is basically a way for us to look at something that calls something we don't control and then extract the call to that something. Let's say it's a logger and make it into a separate method that just contains the call to that logger and then we inherit the class the actual class that contains the call. So if the class is string calculator (external link, opens in new tab) and it calls a logger, we actually inherit

[02:51] (external link, opens in new tab) In our tests from string calculator and we override the virtual method we just extracted. It sounds kind of complicated, but it really isn't. So let's say that we have this class. It's a class called static logger. It has a static method and we obviously can't use it. And what we'd like to do is to prove that is to prove that we actually call static logger when we should.

[03:23] (external link, opens in new tab) So in our what I'm going to show you is a class called string calculator with statics. And in there there is already a call to static logger. So now we're kind of getting into legacy code (external link, opens in new tab) land a little bit. How do you prove that maybe there is a bug in the call? Maybe there's a missing space or an extra space that shouldn't be there. And we want to prove that before we change the code in the call to the static logger. So one way we can do this without using things like PowerMock is by extracting just that line where we call static logger into its own little method.

[04:01] (external link, opens in new tab) Before we use PowerMock, let me just show you how we can achieve this without things like power. So here we have the class and this is the line that we care about static logger.write but I want to make this class more testable. Obviously I cannot test that static logger was called with regular methods. So what I'm going to do is I want to be able to write this test

[04:31] (external link, opens in new tab) Add always calls the static logger. That's what I want to prove. Maybe it doesn't, maybe it should, but I'm not even sure where to start. So, I'm going to make it testable first. And when we deal with legacy code, some of the rules of TDD (external link, opens in new tab) don't make sense anymore. For example, sometimes you have to refactor your code before you can test it as a unit test (external link, opens in new tab). But of course, before you can refactor your code so you can make it testable, you have to have some kind of integration tests (external link, opens in new tab) so that you know you didn't break anything by refactoring to make it testable so that you can test it.

[05:03] (external link, opens in new tab) See, it's a very simple idea. You start with integration test so that you can refactor your legacy code and then you can add unit tests. Okay. So what do I do? I just select this line and I extract the method. Now notice that the string that I sent to the logger is not I took the int and the string and I made it into a single parameter because what I don't want to happen is that in the virtual method I'm going to create I don't want there to be any logic whatsoever

[05:39] (external link, opens in new tab) Because if there is logic I'm going to be overriding that in my test. So I won't be able to test that logic. So what I what I'd like to do is to also know that the text being sent is correct. So I don't want to have any logic in my tests and I want to not override any logic in my tests. So I'm going to make the extract method the simplest method I can think of. So I'm just going to call it call logger.

[06:11] (external link, opens in new tab) So I have this method call logger. Now I have this call. I'm going to make it protected so I can override it. And that's basically it. You know this is a very very simple method. I have call logger here. And the idea of refactoring is changing the code without changing the functionality. And the code does exactly the same as it did before. The only difference is that now I have a way to to test it. So what I've done actually is created what what's called a seam (external link, opens in new tab).

[06:45] (external link, opens in new tab) A seam is when you have for example fabric and you want to connect two pieces of fabric. So all our clothes are made of many many seams. It's where things connect to each other. So a seam is also where you can inject or replace or override dependencies in your tests. The more seams you have in your code, the more testable it is basically. So here we've created a seam because we have a method that is virtual by default that only handles the dependency.

[07:16] (external link, opens in new tab) Yes of course I could have overridden parse single number but then I wouldn't be able to test all the logic inside it. So I don't want to override that. I want to only create a method that I override that will break the dependency (external link, opens in new tab) to the static logger. And now just like I did with a mock object (external link, opens in new tab), I can assume that I have this what's called a mock method. So I assume that if the method got called, then the static logger got called. So how do I do that? If I go to my test, the first thing I'm going to do is that in my test class, I'm going to create a new class and I'm going to call it testable

[07:53] (external link, opens in new tab) String calculator with statics because that's the name of a class that I'm overriding. I'll say I'm extending string calculator with statics. Now this is the first time we've done this in all of this course is that we actually inherit from the class we are about to test. But we are going to inherit all the implementation of that class and we're only going to override a single method in that class.

[08:30] (external link, opens in new tab) So here I might say override and then paste that in. And now I can override that method. And notice that I'm using the override annotation here. This is just an optional annotation that I can use so that my compiler will tell me if I got the name of the method wrong for example. So if I do this I can see a little error here.

[09:01] (external link, opens in new tab) There is no such method. It's just a helper for me. Okay. So I override this and now I'm going to treat it just like we did with all the fake classes we hand written before. I'm going to take the text. I'm going to say written equals the text we just got. And what is written? Let's just make it a field on the class. Make it public so that we can assert against it. That's about it.

[09:32] (external link, opens in new tab) So now we have this whole class and that's the class we're actually going to use in our tests. So now in my test I'm going to create a testable string calculator with statics new testable

[10:03] (external link, opens in new tab) And once I've created I can basically say let's see did I get it wrong? Yeah, I should have said string testable mistake. Now the next thing is I'm actually going to call SC.add because that's my requirement. And last thing I have to do is just assert equals that I get back one to

[10:34] (external link, opens in new tab) The written. So this is almost like closing a circle. It goes back to when we just started with using our own little fake classes. Only this time the fake class is the real class, but the only thing fake about it is a single method. Everything else is the real implementation. So we break the dependency on the static logger by overriding the method that calls it. And so if the field has one, we can assume that the static logger method actually got that text because there is very little logic that we override.

[11:12] (external link, opens in new tab) We can be pretty certain that things are okay. This is called extract and override questions. answers. Does anyone want me to repeat this? >> Yeah, the call logger. This is what it looks like. >> Yes, this is the extraction and this is what it looks like. >> You had to do the extraction because you couldn't override the static right directly.

[11:42] (external link, opens in new tab) That you can override the call. >> I had to do the extraction because not because I couldn't override the call to parse single number but I couldn't override a call to a static logger. You cannot override a call to a static method because there is nothing to replace. There is no instance of anything. So whenever we have something like that and by the way this works for anything creating a new logger creating a static logger calling a database anything you can extract the call to that dependency as simple as possible and then override that method that allows you to break anything you want.

[12:19] (external link, opens in new tab) Now, this is so easy to do once you get used to it that I call this extract an override because it's you're like addicted to it. Once you go into legacy code, this is much easier to do than extracting an interface into the system because it's much less work. Extracting an interface into the system makes you extract a whole new class. This allows you to just override a single method. This is also good for cases where you want to have a method that has a lot of logic, a lot of checking like validation.

[12:51] (external link, opens in new tab) You want to check that the person name is that and that the XML is this and the database is there and all these five things maybe have to be true. And you can override each of them make them each virtual methods and then override them like flags and say create a testable version where this flag will return true. This flag will return false. This flag will return true. Let's see what happens. So you can play with it in a very very easy way. But you have to learn how to do it first. More questions.

[13:27] (external link, opens in new tab) Okay. So we're going to continue from where we left off yesterday. But from the part that where it says after demo for extract and override.