Lesson 9 of 14

Using Extract & Override

Duration: 21:33 Watch on YouTube (external link, opens in new tab)

Markdown version

In this session Roy covers the Extract & Override refactoring (external link, opens in new tab) technique, and when it might be useful in legacy code (external link, opens in new tab) situations.

In this lesson

Roy Osherove introduces extract and override (external link, opens in new tab), from the book Working Effectively with Legacy Code (external link, opens in new tab), on a login manager calling a static logger: the call moves into a protected virtual method with as little logic as possible, which a testable subclass (external link, opens in new tab) overrides. He extends it to simulate a logger exception, check a static web service call and control the machine name rather than build the expected value dynamically (external link, opens in new tab). He also uses it to return fake (external link, opens in new tab) objects, compares it with Moq's (external link, opens in new tab) protected API, and discusses keeping extracted methods protected or public.

Transcript

2,932 words

[00:25] (external link, opens in new tab) Now, what we're going to do is we're going to take a look at another technique. And it's called extract and override (external link, opens in new tab). And this technique is something that I I first learned about this in the book called Working Effectively with Legacy Code (external link, opens in new tab). And now we're kind of jumping into the realm of what happens when you have code that is not very testable and you don't want to use one of the profilerbased frameworks to test it. Okay, how do you refactor the code to be more testable?

[00:57] (external link, opens in new tab) So here we have the example of login manager with statics. Now the problem here is that I want to test that static logger.Write gets called but I don't have a way to inject a dependency into this. So what I'm going to do is I'm going to refactor the code to make it more testable without changing the functionality. So refactoring the definition is changing code without changing functionality. So making it more testable, more readable, maintainable as long as it does exactly the same as it did before.

[01:31] (external link, opens in new tab) Nothing more, nothing less. So extract and override is built on top of the idea that you can create a a function, make it virtual, then you can override it by inheriting from the class in your tests. So instead of checking that stat static logger.Write got called, I can do the next best thing. I can extract a method from this control alt m let's call it call logger

[02:06] (external link, opens in new tab) In the method I'm going to make it protected and virtual it cannot be static that way I can override it in my derived classes in my tests and another another important part is that I want want the method to be to have as less logic as possible. It has to be a simple and stupid method because if it has any logic including the string here in my test, I'm going to be overwriting this method. So there is no way for me to check that the logic inside it is correct.

[02:41] (external link, opens in new tab) So I'm going to get everything the method needs as a parameter. Ctrl AltP to extract the parameter. Let's call it text. And now up here we have the method. This is an earlier version of the same method. So we have this and we have call logger and they're connected to each other. So instead of checking that call logger the static logger got called I can check the next best thing that call logger got called and I assume that if this got called then static logger itself got called.

[03:19] (external link, opens in new tab) That's the next best thing. So the way I break the dependency (external link, opens in new tab) is by creating another layer of indirection in my application. Now in my code I can inherit from the class. So I can create what's called a testable login manager.

[03:49] (external link, opens in new tab) I like the word testable in the beginning because it tells me that this is not a regular fake (external link, opens in new tab) class. This is a version of the real class that I'm testing, but it's a more testable version. And in this testable version, I've broken a dependency to make it more testable. So instead of login manager with statics, this is a testable login manager with statics.

[04:20] (external link, opens in new tab) I'm just going to change all these change all testable login manager to this. So my test looks like this. In the testable login manager I override the call logger but nothing else. That means that instead of a fake class I actually have just a fake method inside the real class. Everything else around this method behaves just the way it did. But this method itself isn't really calling the static logger. It's just saving the value

[04:52] (external link, opens in new tab) Just like we do with every other just like we do with the every other fake class. In this case, it's just a fake method. We save the last log message as a public field. Testable login man me manager with statics. I then create it in my test. Instead of the real class, I create the testable class. I call as login. Okay. And then I use StringAssert to prove that the lm.last message

[05:25] (external link, opens in new tab) Contains what I'm expecting. And this is the basic idea of extracting override. I can take this a bit further. And you'll see just how powerful this idea really is. First of all, it doesn't force me to introduce a new interface into the system. So it has very little impact and also I can I can break with it almost anything. Let's say that I wanted to prove that not only logger gets called but in a separate requirement I would have if call if the logger throws an exception I wanted to catch it and call the static web service.

[06:08] (external link, opens in new tab) Okay. So I can take the static web service and I can prove this was called just like I did before. extract call web service but this time instead of getting the logger exception I don't I don't want to have this logic inside so let me just make this protected virtual

[06:39] (external link, opens in new tab) And instead what I'm going to do is I'm going to extract this whole thing as a parameter because if I extract fact this is a separate parameter and this is a se separate parameter the plus here is actually a piece of logic I won't have any test that proves that I'm connecting these two things so instead I'll extract both of these as the expected message and I don't need the logger exception anymore like this so now this method is again pretty stupid

[07:12] (external link, opens in new tab) And all the logic of connecting the message in the current environment machining is in the call to the web service. So now if I wanted to I could have a test that uses testable login manager but this time if I wanted to simulate an exception I could put another field public exception to throw equals null.

[07:43] (external link, opens in new tab) And now I could say something as simple as if to throw is not null then throw whatever it is you need. And now I need to also override the other method call web service and not call the base because then I wouldn't have done anything. I didn't break anything. But here I can just save to let's say last web service

[08:18] (external link, opens in new tab) Message equals text. Let's make this field. Make it public. That's all I'm doing. And I'm going to assert that the text here is going to be saved to to be saved. So if I write this as a new test, I can say is login. Okay. But the logger throws an exception.

[08:48] (external link, opens in new tab) Then it calls the calls the web service with message and machine name. Okay. So now again I will use the testable login manager with statics and I will say lm to throw equals new

[09:19] (external link, opens in new tab) Exception or whatever I want to simulate. Dude, this is totally a fake exception. Then I would still do the same thing is login okay with something and pass

[10:00] (external link, opens in new tab) Dude, this is totally a fake exception. We can also just contain the current machine name. This is where things begin to get interesting. I can use the current machine name, but I don't want to do this. Plus environment machine name. Why is this not a great idea? H >> yeah well u

[10:32] (external link, opens in new tab) The machine name will be different you may want it somewhere else and and yeah >> exactly the machine name is going to be different on every different machine now that's not a bad idea per se but the problem is that that means that this test is going to be a different test every time it runs on a different machine and what we want from a Unit test (external link, opens in new tab) is consistency. So we want to control the machine name so that it always is the same. If you wanted to prove that we can that the machine name local is working etc.

[11:05] (external link, opens in new tab) We would use an integration test (external link, opens in new tab). But for now this is this is the beginning of our solution. We can choose that it contains but if we knew the machine name in the first place we wouldn't need to do this because this is logic inside my test and most likely I'm also repeating production code from here. So this is really important. This could be a big problem. If there is a bug here for example a missing space and it's also missing in my production code I'll never find out because my test is also expecting no space.

[11:39] (external link, opens in new tab) So, I'd rather have fake exception on machine name. This is much better for me, more readable and expectable. How do I control the machine name? Well, I go back to the blogging manager with statics and I I'll use extract and override again because environment domachine name machine name is just a static property.

[12:12] (external link, opens in new tab) A property is just a static method. So I can go ahead and extract that again. Extract to a method get machining. I like methods better than properties. I don't know why. It just feels righter, more more more correct. And again, make it protected virtual. And now I can have full control of the machine name here in the testable version. I can override

[12:44] (external link, opens in new tab) Get machine name. Now I could say machine name, but that would be a problem. It would be a problem because the reader of my test is going to look and say blah blah blah blah on machine name. Where the hell did machine name come from? So in terms of readability (external link, opens in new tab), it's going to look like a magic string. And I want my test to be fully readable. So I don't want my testable and fake classes to have this kind of logic inside them because it hurts readability and I don't want the reader of my test to look in two places to understand what's going on.

[13:23] (external link, opens in new tab) So instead I will tell it to return machine name. So my test is going to say this lm dot machine name equals machine name. Let's make it into a field. That's it. I'm not even going to use a property again because a field is simpler than a property in my tests.

[13:56] (external link, opens in new tab) So now I can read the test. I can see machine name here and I can see it here. I can see this here and I can see this here and I can fully understand this and I don't have a problem with the the fact that I'm repeating myself in this test because I'd rather repeat myself than hurt the trust (external link, opens in new tab) of creating something dynamically that I assert against. I don't want I'd rather not have bugs and this is not a big maintainability issue and it definitely helps the readability. So, as we've seen, extract and override is really powerful and it's actually very simple to do.

[14:31] (external link, opens in new tab) You just have to get used to doing this. Now, most of the frameworks out there support extract and override. As we've seen, I have an example here with Moq (external link, opens in new tab). If you had such a thing with Moq, you could do mock (external link, opens in new tab) of let's say login manager with virtual method and lm.protected because it method is not public. setup write to log to to accept this and then it gets called but we can also say dot

[15:04] (external link, opens in new tab) What was it we can also tell it to return various things sorry doesn't really matter how to do it right now the the the the the important thing is that this API exists in all of the frameworks so if you wanted to you can use the framework to do it if the method that you we extracted we would make it public then it would work just the same as any other method that we can override. Okay. Personally I hate this API inside inside the Moq because

[15:41] (external link, opens in new tab) If I wanted to verify it I have to say verifiable here and then I have to verify here. It's kind of a problem. So most of the time I actually use the testable version of something and if there are a lot of methods to override in a testable version then I would use one of the frameworks. Are there any questions about this before we continue? I'll show you another case where this is really helpful. Let's say that we don't have just a static method that let's say that we have a future object.

[16:16] (external link, opens in new tab) We can use extract and override not just to fake a value but we can actually use it to return a fake object. Let's say that we could if we wanted to inherit from real logger if we could do that we could extract this thing into a virtual method that returns anything that inherits from real logger or ILogger in this case. So we can say extract method like a get logger and this method

[16:46] (external link, opens in new tab) Public public static would make it to protected virtual ILogger get logger. By default it returns the real logger but then in our testable version we could return a fake logger. So then anyone who wants to get an instance of a logger actually gets an instance of a fake logger. So extract an override can be used not just to check that the method got called. It can also be used to return fake objects and fake values. Another example

[17:17] (external link, opens in new tab) We can use it just like we use it for environment name we could have used it for configuration settings or for or here we can actually give ourselves two options the same as here. We could either get return here a fake web service or we could fake the whole thing here into a virtual method. So we have two choices of which scope we want to fake the do we want to return a fake object with an interface or do we just want to prove that some virtual method got called?

[17:50] (external link, opens in new tab) The choice is up to us but both of these will be really easy to do. Okay. Now, most of the time I will check that the method got called because it's easier than returning a fake object, etc. It makes test more readable. But I still don't have really great ideas on when to use which. Sometimes something feels a bit wrong with just overriding the whole thing, but at the end of the day, it's almost the same.

[18:20] (external link, opens in new tab) It should making sure that everything gets sent in with the correct string, etc. Both of these will work just the same. So you can use this as really really powerful technique to to work on with a lot of legacy code (external link, opens in new tab) bases. Another option where I've seen this really helpful is if you have a method with a lot of logic inside it that gets that depends on a lot of decision- making like if this is true then do this, if this is true then do this or a method that does a lot of verifications like validations on an email or details and so then you can just extract and override each one of the validations.

[18:56] (external link, opens in new tab) So that you can set set a lot of flags very easily from the test. You can say in the test this validation will return true, this validation will return false, this will return false. Now let's see what happens. So extracting override is really powerful for that kind of stuff as well. Yes, >> one question. If we have the way in extracting to a protected method, we have to we either get the ugly API in case of Moq or it's it's not an issue if you have our own test version of that class.

[19:28] (external link, opens in new tab) But if we if we say it's it's not an issue for us and we make it a public method, we can just use it and it's virtual. We can we can just use it with a normal way of of using the marketing classes. Yeah. >> Yes. >> So the it really I I see see a big point with a special test version. So that sort of thing. But you would say just yeah. Yeah. I still not decided what I would prefer more just because otherwise we get a lot of classes just for these test versions.

[20:05] (external link, opens in new tab) And other hand we just have it potentially repeated in the in a test. So and >> so you're you're still undecided whether you like keeping things protected or keeping things public. >> Well personally I I I like keeping them protected because I don't want to make too many methods public >> because it kind of spoils the API. On the other hand yeah because the point is that this method signifies to me that an external resource got called.

[20:36] (external link, opens in new tab) >> Mhm. >> and if it's public it's like calling the external resource directly. >> So you would have in that public API one method that eventually calls another public method. >> Yeah. And so it it kind of feels weird in terms of design, but so if if it wasn't meant to be public in the first place and is just to satisfy a compiler, I'm not sure it's a good idea. >> Yeah. Yeah. >> But I could be wrong.

[21:06] (external link, opens in new tab) I still I'm still in dilemas about this myself. >> Other questions? Okay. So continue the exercises from login manager with statics. Okay. Don't use any of the frameworks for now. Just use extract and override.