Vagif Abilov's blog on .NET

TypeMock Isolator and matching faked method’s arguments – part 3

Part 1

Part 2

I promised to address matching reference and output arguments using lambda-based syntax that I proposed for TypeMock Isolator. Before getting there I would like to retract my original comment that faked method’s arguments should be matched by default. I still think that, like strong typing, strong matching is a best practice to follow. However I should not forget the main goal of my proposal: increase code simplicity and compactnes. My first examples used very simple method calls consisting of one-two arguments, so an extra effort of typing “(int x, int y)” went unnoticed. Using real API methods immediately showed how inconvenient it can become to faked methods with large number of arguments if developer is forced to list argument types even when no argument match is required. Here how it might look:

Isolate.WhenCalled((string a, Evidence b, byte[] c, AssemblyHashAlgorithm d) => 
        Assembly.LoadFrom(a, b, c, d)).WillReturn(null);

Note where it gets really terrible: you have to type the whole argument list of LoadFrom: “string a, Evidence b, byte[] c, AssemblyHashAlgorithm d”. All this without help from Intellisense! The argument list comes before the method name.

Now compare it with current use of TypeMock API:

Isolate.WhenCalled(() => Assembly.LoadFrom(null, null, null, 0)).WillReturn(null);

What would you choose? With all respect to strong matching the choice is clear.

Eli suggested defining a different method for faking a method a method when arguments matter, and now I came to the same conclusion. So I will leave WhenCalled alone and use a different method name, let it be called WhenCalledArgs (I am not sure about using word “Exact” in such method name, because this method will support partial match, and in case you only match one argument out of five, the word “exact” can be confusing).

Now back to agenda: matching reference and output arguments. In my previous post for each size of argument list I defined two overloads (I will be using new name WhenCalledArgs):

public static IPublicNonVoidMethodHandler WhenCalledArgs(Func func);
public static IPublicNonVoidMethodHandler WhenCalledArgs(Func func, Func predicate);

The definitions above cover the case of a method with single argument. The first overload corresponds to a call with no argument match and no checker. The second overload can be used to define a custom checker for an argument. What is missing here is support for reference and output arguments, when the passed value is assigned to an argument marked as “ref” or “out” on method return. Fortunately, this is easy to achive using the same notation. It just requires new oveloads.

To assign reference and output value we need to extend WhenCalledArgs with a new parameter: an  Action<T> delegate:

public static IPublicNonVoidMethodHandler WhenCalledArgs(Func func, Action output);

And to enable specifying both custom checker and output delegate, we will have an overload that combines them all:

public static IPublicNonVoidMethodHandler WhenCalledArgs(Func func, Func predicate, Action output);

Similar overloads we will need to provide for void methods (in such case WhenCalledArgs will be returning IVoidActionHandler instead of IPublicNonVoidMethodHandler). Now let’s look at the test code. For the sake of simplicity we assume to be dealing with a static class called MyClass, and the method that we are going to fake has the following signature:

public static void LookupCustomer(int customerId, out string firstName, out string lastName);

First we will fake its call but assign on return the values “John” and Smith” to the firstName and lastName respectively. Here how the code will look:

Isolate.WhenCalledArgs((int x, string y, string z) => 
        MyClass.LookupCustomer(x, out y, out z), (x, y, z) => { y = "John"; z = "Smith"; }).IgnoreCall();

Note that the customerId argument will not be matched. If we want to match it we slightly change the code:

Isolate.WhenCalledArgs((string x, string y) => 
        MyClass.LookupCustomer(123, out x, out y), (x, y) => { x = "John"; y = "Smith"; }).IgnoreCall();

What happens now is that the method LookupCustomer will only be faked for customerId equals 123, and upon return firstName and lastName arguments will be assigned values “John” and “Smith”.

Using “ref” instead of “out” does not really change much. It just gives an opportunity to assign (and match) a value to an argument and in addition specify another one on return.

And of course we can combine assignment of ret/out values with custom argument checker. Here’s an example:

Isolate.WhenCalledArgs((int x, string y, string z) => 
        MyClass.LookupCustomer(x, out y, out z), (x, y, z) => x > 100, 
        (x, y, z) => { y = "John"; z = "Smith"; }).IgnoreCall();

This is quite compact notation, however it does various things:

  • It fakes calls to MyClass.LookupCustomer without argument match
  • Even though arguments are not matched, only calls with customerId greater than 100 will be faked, as speficied in custom checker predicate
  • On return output arguments firstName and lastName will be assigned values “John” and “Smith” respectively.

Conclusion

In these blogs posts I tried to demonstrate how we can take advantage of C# 3.0 language features and provide compact notation for TypeMock Isolator AAA syntax. I am pretty sure there is someting uncovered, and I haven’t even thought about how this approach can be applied to VB (I am afraid it can’t, at least not until Visual Basic gets action-based lambda expression support). However I believe this notation can be successfully applied to Isolator AAA syntax, as it is based on the same principles and can further improve compactness and readability of the code.

Comments

Arthur Shamsutdinov said:

Thank you for good article! But on my point Moq is better. You can use exact arguments match, argument conditions, and unconditional arguments using same syntax.

# March 16, 2009 11:49 AM

Vagif Abilov said:

Arthur,

Moq is a very nice framework, sufficient if you use code that is designed for testability. If you need to mock the code with static classes/methods, sealed classes or classes without public constructor, TypeMock often is your only option. And I often deal with such code.

Here's more on this topic:

devlicio.us/.../typemock-powerful-but-needed.aspx

# March 16, 2009 1:53 PM

Arthur Shamsutdinov said:

Yes, this is usually question of coding style. For example in my last project 90% of code is working with interfaces, not with classes.

# March 16, 2009 2:16 PM

Vagif Abilov said:

Even if you have full control over the code you use, I have mixed feelings about this. Would you inherit string class from IString interface? Make it non-sealed? Always have Log non-static?

There are static and sealed classes for good reasons, not for the reason of sloppiness. And we have to deal with them.

# March 16, 2009 2:48 PM

Arthur Shamsutdinov said:

Why mock strings? I've never faced such cases. We are using IoC (Unity) to create log.

Yes we are unable to use static classes but usually it is not a problem (again we solve it using Unity).

# March 16, 2009 3:13 PM

Vagif Abilov said:

Well, string is was just an example, don't take it literally. What I meant that in a large system there will always be something that is static or sealed or does not have public constructor. In addition, I think there is a place for such things in our own code. Then TypeMock is your friend.

# March 16, 2009 3:20 PM

Arthur Shamsutdinov said:

Ok, I agree, we can't have sealed classes (we solve it with interfaces (but we have to mock hole class, that is usually the best way)), we can't have static classes (we solve in with Unity-based singletons), and I hope this is not the worst way.

# March 16, 2009 3:32 PM

Moran said:

Thanks for the write up!

# March 27, 2009 8:31 AM

Sarbjit Hanjra said:

Thanks You for nice article, our application is based on CSLA so we are using static methods all where. We are start to implement TypeMock in our unit testing, please suggest me how to mock method of another class in my testing method, please see the following example ..its not working as expected.

public class SomeClass

{

 public int Add()

 {

   return 11;

 }

}

----------------------------------

public class MyClass

{  public int AddCode()

 {

   SomeClass obj=new SomeClass();

   return obj.Add();

 }

}

----------------------------------

[TestMethod]

public void AddCodetest()

{

int result;

MyClass myClass = Isolate.Fake.Instance<MyClass>(Members.CallOriginal);

SomeClass someClass = Isolate.Fake.Instance<SomeClass>(Members.CallOriginal);

Isolate.WhenCalled(() =>  someClass.Add()).WillReturn(15);    

           result =myClass.AddCode();

           Assert.AreEqual(15, result);

}

# August 18, 2009 8:11 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Please add 7 and 4 and type the answer here: