chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
I am trying to verify that a method was called and a parameter fits a certain condition using TypeMock 6.2.5.0 and VS2010 SP1. Unfortunately, the delegate I pass to the .Matching() method receives a zero-length object array, even when the method accepts parameters. Below is a repro of the issue:

public class Logger
{
    private static readonly Logger s_Instance = new Logger();
    public static Logger Instance
    {
        get { return s_Instance; }
    }

    public void LogError(string format)
    {
    }
}

[TestClass] [Isolated] public class CrazySingletonTests
{
    [TestMethod]
    public void Test1()
    {
        Isolate.WhenCalled(() => Logger.Instance.LogError(null)).IgnoreCall();

        Logger.Instance.LogError("We've got a problem!");

        Isolate.Verify.WasCalledWithArguments(() => Logger.Instance.LogError(null))
            .Matching(args => { Assert.AreEqual(1, args.Length); return true; });
    }
}
asked by allon.guralnek (10.6k points)

8 Answers

0 votes
Hi Allon,

The problem here is that because you are using chain of calls i.e: Logger.Instance.LogError
your callback was called on the Logger.Instance property instead of LogError method.
The workaround is to break the chain like this:
Isolate.WhenCalled(() => Logger.Instance.LogError(null)).IgnoreCall();

Logger.Instance.LogError("We've got a problem!");
var instance = Logger.Instance;

Isolate.Verify.WasCalledWithArguments(() => instance.LogError(null))
    .Matching(args => { Assert.AreEqual(1, args.Length); return true; });


I can't see a way that the Isolator can guess on what method in the chain your callback should be called.
Consider the case:
Isolate.Verify.WasCalledWithArguments(() => A.Method1(arg1).Method2(arg2)).Matching(args => ...);

On what method the user callback should be called? Method1 or Method2?
I think the right thing to do here is to throw an exception that explains the problem and the correct workaround.
answered by ohad (35.5k points)
0 votes
If chains of calls aren't supported, why does the following test pass? How does Isolator guess for which method in the chain my delegate should be called?

[TestMethod]
public void Test2()
{
   Isolate.WhenCalled(() => Logger.Instance.LogError(null))
      .DoInstead(ctx => Assert.AreEqual(1, ctx.Parameters.Length));

   Logger.Instance.LogError("We've got a problem!");
}


And I agree, if something isn't supported by Isolator, it should throw a TypeMockException. It can easily check if the object array that is about to be passed to the delegate is zero-length and know that something is wrong.
answered by allon.guralnek (10.6k points)
0 votes
Hi Allon,
Chained calls are not supported in Isolate.Verify.WasCalledWithArguments.Matching API because of the problem I described above,
it is however supported in WhenCalled API.
Please note that chains has one known limitation which is nested chains.
That means that a method argument inside WhenCalled can not be a call to a method or property.
e.g:
Isolate.WhenCalled(()=> A.Method1().Method2(A.b().c())) ...

In the case above Isolator will throw an exception that suggest to break the call into:
var arg = A.b().c();
Isolate.WhenCalled(()=> A.Method1().Method2(arg))...
answered by ohad (35.5k points)
0 votes
So basically, ♫ anything Matching() can do, DoInstead() can do better? ♫

They seem like very similar APIs with similar idioms, almost identical. I can't seem to understand the dichotomy. What is the essential difference between Matching() and DoInstead() that causes the feature set to diverge like that?
answered by allon.guralnek (10.6k points)
0 votes
Hi Allon,
Not exactly the same :)
DoInstead() is kind of general API that let you do things beyond the standard API. For example you can change the return value of a method based on custom logic in your callback.
Matching() method on the other hand should only be used for checking arguments, the callback returns bool while DoInstead returns whatever value your original method returns.
Also in case of WhenCalled(A.B().C()).DoInstead(...) the callback will be called only on the last method in the chain.
answered by ohad (35.5k points)
0 votes
Are you sure WasCalledWithArguments() isn't supposed to support call chaining? WasCalledWithAnyArguments(), WasNotCalled() and WasCalledWithExactArguments() all support call chaining, in addition to the rest of the AAA APIs. It just seems unusual to me that there is such broad support for chaining, even within Isolate.Verify, yet WasCalledWithArguments() seems to be an odd duck. I understand it might be difficult to discern multiple chained method calls, but when there is a single method call chained to a property (like in my case), there is only one possible method for which its arguments need to be verified and thus no ambiguity.
answered by allon.guralnek (10.6k points)
0 votes
Hi Allon,
Just to make it clear Isolate.Verify.WasCalledXXX methods all support chaining for example this would work:
Isolate.WhenCalled(() => Logger.Instance.LogError(null)).IgnoreCall();
Logger.Instance.LogError("We've got a problem!");
Isolate.Verify.WasCalledWithExactArguments(() => Logger.Instance.LogError("We've got a problem!"));


The only problem is with the Matching method which I think the better solution will be to throw an exception that guide the user to the correct workaround.
answered by ohad (35.5k points)
0 votes
The only problem is with the Matching method which I think the better solution will be to throw an exception that guide the user to the correct workaround.


Either that or add support for chaining in Matching()... :)

Thanks for your help!
answered by allon.guralnek (10.6k points)
...