Lesson 11 of 14

Testing Legacy Code with Typemock Isolator Deep Dive

Duration: 20:08 Watch on YouTube (external link, opens in new tab)

Markdown version

Roy Tests BlogEngine.NET with Isolator

In this lesson

Roy Osherove tests the same blog engine code with Typemock Isolator (external link, opens in new tab), explaining why Visual Studio must be restarted for a profiler-based framework (external link, opens in new tab) and what the Isolated attribute does. He fakes the static methods of the trackback class to verify that Send was called, then follows each failure, which he calls wishful invocation (external link, opens in new tab), faking a non-public static method, the blog settings singleton and all instances of a remote file class. The test passes without any debugging, and he asks the audience to get to the same point.

Transcript

2,283 words

[00:26] (external link, opens in new tab) So now let's look at the blog engine, but this time we're going to try to do it with Typemock Isolator (external link, opens in new tab). So I'm going to show some of the APIs we can use and some of the thought process. We're not going to solve it fully. We're just going to start working with it. So our problem is first of all to to start using Typemock Isolator, I installed it and restarted Visual Studio. It's important to restart Visual Studio because because it's a profilerbased framework. The only way a profiler works is if you have an environment variable in the process where the tests run.

[01:03] (external link, opens in new tab) And what isolator and Moles (external link, opens in new tab) and JustMock do they install a plugin into Visual Studio when loaded simply loads an environment variable. And so all the tests in Visual Studio run with that with the Visual Studio environment variables. And so they run with profiler enabled. If I didn't do this then my test will just fail because the profiler doesn't work. And then I added two references. Basically I went and said right click add reference.

[01:36] (external link, opens in new tab) I looked for T for Typemock and I need the C# APIs and the core DL. So just this these two. If you're looking for them, they're in the Typemock Isolator 62 range act and assert and Typemock dl. Next thing I do is I created a in the unit test (external link, opens in new tab) project I added the class called manager tests

[02:06] (external link, opens in new tab) And I added the isolated attribute. The isolate attribute makes sure that if that if I set static behavior in different locations in different tests then they get cleaned up between each test run. If we forget to do this our test can start behaving in a weird way. I could have instead do done just isolated at the test level and then I would have to remember to do it for each test. If I do it at the class level, it works for all the tests in the class.

[02:43] (external link, opens in new tab) So this is the beginning of the test that I that I that I wrote. If I just want to call manager send I'm I'm still still going to call send. So we'll have this dependency and everything.

[03:15] (external link, opens in new tab) If I was to call manager.send, I need to create a fake (external link, opens in new tab) publishable bull item. So I can just call fake item here and also send in a URI. So we're just going to send in some URI here.

[03:47] (external link, opens in new tab) Equals. And now I'm going to use isolator to fake it. Isolate fake an instance of I publishable. That's it. Now, if I just do this, what is my end? What should be my end result (external link, opens in new tab) here? What I'd like to do is I'd like to be able to ch make sure that trackback.sam got called because that's one end result. Another end result would be to track that pingback.

[04:19] (external link, opens in new tab) So let's start with this. I want to track that trackback. So I should start tracking it by saying isolate fake static methods of type trackback. And the reason I'm not saying just a specific method I could have done this isolate fake or sorry isolate when called trackback send

[04:52] (external link, opens in new tab) With anything ignore since it has to return something we can just say return recursive fake (external link, opens in new tab). It's the same as saying ignore call. Just return whatever it needs to return. The reason I'm not doing this and remember I'm sending in here null because by default isolator doesn't care about parameters. If I wanted it to care about the parameter I would have to say with exact arguments

[05:24] (external link, opens in new tab) And then it would care. Right now it doesn't care about the parameters. So it always returns a recursive fake. In this case it returns always a default value. I could have done this, but I might as well fake all the static methods of trackback if I don't care about it really because it's a different class and it's a different location. I might as well fake all of them because I don't care about any of these static. So, I'm just going to do that.

[05:57] (external link, opens in new tab) Now, if I wanted to verify it got called, I can say isolate.verify And then I can choose for example I can verify that something non-public got called or if it's public I can say it was called for now we can say with any arguments trackback. And if it didn't get called my test will fail. So if I just run this,

[06:46] (external link, opens in new tab) Okay. Now just to prove that we can get this to to be called. Even if I in my test I call it directly just to prove a point can say trackback. send with anything or just a fake item. Who cares? So, trackback message, sorry. New track back message with the fake item and the new URI

[07:17] (external link, opens in new tab) With something. Did I miss anything? Too many braces. H >> the constructor has three parameters. >> Oh yeah, it has two URIs, right? Yeah. And again a new URI. So I'm just going to do this for now.

[07:50] (external link, opens in new tab) Ctrl + D to duplicate. And hopefully now we will end up with something. Or you know what? just say this. Well, that's it because it doesn't matter for our demo purposes. I'm going to repeat the test. I pressed alt R and you can see here one passed. So, it actually got called. I remove this.

[08:23] (external link, opens in new tab) I alt R again and the test fails. Okay. So now that we can track it, we can start doing interesting things. If we actually run this, our test fails because it didn't get called. But now we can go deep and find out why it's not called. What should happen? If you go into manager send,

[08:54] (external link, opens in new tab) We'll see that somewhere we have a get URLs from content. Okay. Now, right now it's protected virtual because I I I I refactored it. What if I turn it back into static? Private static. Get yours from content. And just repeat the test for now. Build succeeded. Still failed. Okay, if I wanted to control get URLs from content so that I get back some URL because it's non-public,

[09:31] (external link, opens in new tab) I can either turn it into public or I can use one of the these APIs. Isolate when non-public dot when called. Now the target object is there is no instance because it's a static method. So it's a type you can just say type of manager

[10:03] (external link, opens in new tab) With the method name of get get urls from content and I can also tell it what to return when called will return. And now I can return a new list of URI URIs. So if it's expecting get URLs from content which is an innumerable URI

[10:34] (external link, opens in new tab) We can say list and create a variable of list of URI equals new list of URI and initialize it with a couple of them or maybe just I http

[11:11] (external link, opens in new tab) So we have this new list. We don't even need this for now. This list is going to be returned. I just make it even more readable. I think right now get else from content will return list. Now let's see what happens. So now I get a a weird stuff. System null reference exception object reference not sent to an instance on of an object. If I look at the stack trace I can see blog engine core get current instance.

[11:41] (external link, opens in new tab) If I go back double click here blog settings.get instance and then somewhere here I have if blog settings instance enable trackback send. So I've come across another dependency and this is a very common pattern of work in legacy code (external link, opens in new tab) that we've talked about which I call the wishful invocation (external link, opens in new tab). So again basically what I'm doing is I'm trying to write my test here

[12:13] (external link, opens in new tab) And then I invoke something and I wish that it works. If it does it's great. If it doesn't, then I look up at the track race trace. I see what I missed and I continue faking away until I can actually get through the path that I'm working on. So if I've realized that this is a dependency, if I didn't have isolator, I would have to extract and override (external link, opens in new tab) this or something. But here now what I can do, I have several choices. I can just say

[12:46] (external link, opens in new tab) Isolate when called blah blah blah ignore call or return recursive fake or I can just say will return true because I wanted to enable trackback send and this will take care of everything. If I didn't care about the result, I could have just faked all the static methods on the type of blog settings.

[13:18] (external link, opens in new tab) That would make things even better for me because then I wouldn't care about each specific method. But right now, what you can see is that I can fake a chain of things. Blog settings has an instance and that will be fake by default. So if I run this again, no debugging yet. I look at the output and now I have another exception. The remote name could not be resolved.

[13:49] (external link, opens in new tab) Something.com get response. So it's actually going out to the web and trying to download something because it's going through a remote file. So if I go here, it's saying remote file get file as string and then get trackback URL from page. So these two lines are actually a dependency as well. These are actually downloading the the content of the file. So how do I take over instances of things that are mued up?

[14:22] (external link, opens in new tab) One way to do it would be to just use the isolator future syntax. So we can say we could create a fake instance of remote file. Or so the first way would be to say isolate fake instance of remote file file. Now remote file currently is internal.

[14:54] (external link, opens in new tab) I'm going to make it public for demo purposes. But in real life, what you would probably do is you would use the InternalsVisibleTo attribute (external link, opens in new tab) in the in the production code so that your test will see it as if it was public. So isolate fake instance of remote file. Let's get back the instance fake remote file. And now I can say isolate fake swap

[15:26] (external link, opens in new tab) All instances of remote file with the fake file. I could have done instead isolate swap all instances on remote file with a recursive fake and then I wouldn't need the file at all. So this is like a shortcut. If I don't care about the specific behavior of the fake object,

[15:58] (external link, opens in new tab) Then I can just say, "Look, fake an object for me and replace all the future objects with a fake one that just does all the defaults. If I care about how that fake would behave, then I would fake it on my own and I would tell it to replace it with my own." Okay, so what happens if I just fake it with with a recursive fake? I run the test again, go through the output, I get a verify exception was expected but was not called. Now I'm not getting an exception.

[16:30] (external link, opens in new tab) So I'm not I don't know what the dependency is or not, but I might be just not going through the path as as as well as I thought. So now might be at the time to either debug or if I look at this get trackback URL from page it might be that it's getting a page content which is empty from that fake file. So what I would want to do maybe is to say get track URL from page. I can either go there and

[17:04] (external link, opens in new tab) Make it match a specific regular expression as the fake content. And let's see what the regular expression is. It looks for trackback.ping blah blah blah blah blah blah blah. And what if it's too complicated? What if creating the content that is so complicated for this is actually has become so complicated it's a dependency. Well, in that case I can just fake the validation of the regular expression.

[17:38] (external link, opens in new tab) So instead I'm just going to fake this get track URL from page is going to return some kind of non-null trackback URL. Okay. So this is going to I'm going to make this return non trackback URL. And this was initially static as well. So I'm going to make it static back again. Private static. So again I'm going to go here and say let's duplicate this and say when called on manager

[18:09] (external link, opens in new tab) What was the name of it? Get track your from page. We'll return new what was it? What's supposed to happen? Supposed to get a track URL. What's the type URI? Okay, it's going to use that URI. So, I'm just going to send in a new URI

[18:44] (external link, opens in new tab) URI that is keep forgetting the HTTP I don't know why must be a psychological thing. something.com. So now get track URL from page is going to return something that's not null. Repeat the test. Alt R. Let's see the output. And now it actually succeeded. And I didn't do even a single bit of debugging yet.

[19:14] (external link, opens in new tab) Okay. But at some point in legacy code, I would get into the idea of debugging. Sometimes you do have to debug. Are there any questions so far? So I didn't touch the code except actually make it harder to test so that it would feel a bit more like legacy. Are there any questions so far? Okay. So, what I want you to do, I want you to install Typemock Isolator. And I want you to get to this stage

[19:45] (external link, opens in new tab) Where you can actually prove that trackback.send got called. Okay,