In our forums, Soon Hui Ngu asked if there’s a way to fake recursively entire assemblies, without too much hustle. And the answer is – almost! In the current implementation of CThru, you can’t do it recursively. But you can use CThru to do almost that. You can fake all types almost without any work. Here’s how:
First define an Aspect (which conveniently derives from CThru.Aspect). In my case, I’m interested in changing behavior of methods of different assemblies. So here’s my class:
1 |
<span class="kwrd">public</span> <span class="kwrd">class</span> DependentAssemblyAspect: Aspect<br /> {<br /> <span class="kwrd">public</span> <span class="kwrd">static</span> List<Assembly> AssembliesToFake = <span class="kwrd">new</span> List<Assembly>();<br /> <br /> <span class="kwrd">public</span> <span class="kwrd">event</span> Action<DuringCallbackEventArgs> OnMethodBehaviorCallback =<br /> e =><br /> {<br /> <span class="kwrd">foreach</span> (Assembly assembly <span class="kwrd">in</span> AssembliesToFake)<br /> {<br /> <span class="kwrd">if</span> (assembly.GetType(e.TypeName) != <span class="kwrd">null</span>)<br /> {<br /> e.MethodBehavior = MethodBehaviors.SkipActualMethod;<br /> }<br /> }<br /> };<br /><br /> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> MethodBehavior(DuringCallbackEventArgs e)<br /> {<br /> OnMethodBehaviorCallback(e);<br /> }<br /> } |
Let’s see what we’ve got here. First a field containing the assembly list. I’m going to add Assembly objects from my test to this list. Second, I have an event handler that looks at what method that gets invoked, and selects its behavior based on belonging to the assemblies in our list. In our case, we select SkipActualMethod, in order to fake the methods.
The test looks like this:
1 |
[TestMethod]<br /><span class="kwrd">public</span> <span class="kwrd">void</span> FakeBehaviorsOnTypesFromDependentAssemblies()<br />{<br /> <span class="rem">// Add dependent assemblies to list - current and external </span><br /> DependentAssemblyAspect.AssembliesToFake.Add(Assembly.GetExecutingAssembly());<br /> DependentAssemblyAspect.AssembliesToFake.Add(Assembly.LoadFrom(<span class="str">@"C:MyLibrariesTestClassLibrary.dll"</span>));<br /> <br /> <span class="rem">// Add the aspect to CThru</span><br /> var aspect = <span class="kwrd">new</span> DependentAssemblyAspect(); <br /> CThruEngine.AddAspect(aspect);<br /> CThruEngine.StartListening();<br /><br /> <span class="rem">// Execute from external assembly</span><br /> TestedClass tc = <span class="kwrd">new</span> TestedClass();<br /> tc.exception();<br /><br /> <span class="rem">// Execute from this assembly</span><br /> MyClass.Do();<br />} |
Ok, that works on calls from both assemblies. What if I have this test:
1 |
[TestMethod]<br /><span class="kwrd">public</span> <span class="kwrd">void</span> FakeRecursiveBehaviorsOnTypesFromDependentAssemblies()<br />{<br /> <span class="rem">// Add dependent assemblies to list</span><br /> DependentAssemblyAspect.AssembliesToFake.Add(Assembly.GetExecutingAssembly());<br /> <br /> <span class="rem">// Add the aspect to CThru</span><br /> var aspect = <span class="kwrd">new</span> DependentAssemblyAspect(); <br /> CThruEngine.AddAspect(aspect);<br /> CThruEngine.StartListening();<br /><br /> <span class="rem">// Execute from this assmbly</span><br /> MyClass mc = <span class="kwrd">new</span> MyClass();<br /> mc.GetAnotherClass().DoSomething();<br /> <br />} |
This time I have a recursive behavior. If you run it, you’ll see that GetAnotherClass returns null (which is the current behavior). How can I override that? Let’s update the Aspect to return a recursive fake for that method:
1 |
<span class="kwrd">public</span> <span class="kwrd">event</span> Action<DuringCallbackEventArgs> OnMethodBehaviorCallback =<br /> e =><br /> {<br /> <span class="kwrd">if</span> (e.MethodName.Contains(<span class="str">"GetAnotherClass"</span>))<br /> {<br /> e.MethodBehavior = MethodBehaviors.ReturnsCustomValue;<br /> e.ReturnValueOrException = Isolate.Fake.Instance<AnotherClass>();<br /> <span class="kwrd">return</span>;<br /> }<br /><br /> <span class="kwrd">foreach</span> (Assembly assembly <span class="kwrd">in</span> AssembliesToFake)<br /> {<br /> <span class="kwrd">if</span> (assembly.GetType(e.TypeName) != <span class="kwrd">null</span>)<br /> {<br /> e.MethodBehavior = MethodBehaviors.SkipActualMethod;<br /> }<br /> }<br /><br /> }; |
I’ve added the if statement for the specific method. For that method specifically, I return a fake object. I also set the MethodBehavior property correctly.
So we can, based on method and type, do without lots of specific coding per test. Give it a try, and play with it a little. As I’ve discovered making this example, there are a few things missing here. I’d like to get your input on what can be added.