Moq - passing arguments from setup() to returns()
Asked Answered
G

1

10

I am not sure how to pass arguments from Setup() to Returns() in Moq.

Here is an example:

public static IInterfaceHandler GetInterfaceHandlerMoq()
{
   // Defining the moq
   var moq = new Mock<IInterfaceHandler>();

   // Trying to set up a moq using another moq
   moq.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid>(), It.IsAny<string>()))
       .Returns((Guid guid, string value) => GetProgrammerMoq(guid, value));

   // Return mocked object
   return moq.Object;
}

Note that GetProgrammerMoq() is a library that will return another Moq. Here is the code:

public static IProgrammer GetProgrammerMoq(Guid guid, string instName)
{
    // Create Moq
    var moq = new Mock<IProgrammer>();

    // Set up the returnables
    moq.Setup(o => o.InstanceName).Returns(programmer + "_" + instName);
    moq.Setup(o => o.Guid).Returns(guid);

    // Return mocked object
    return moq.Object;
}

See here that GetProgrammerMoq() needs its arguments to be set up based on what is passed to CreateCommunicationInterface().

My test then tries to get and use the Moq, but "p" is returned as null (because, I guess, my arguments are not passed properly to Returns()).
Here is a draft of what the test is to look like:

[Fact]
public void DoSomething()
{
    IInterfaceHandler test = ProgrammerMoqs.GetInterfaceHandlerMoq();

    Guid? g = new Guid();
    IProgrammer p = test.CreateCommunicationInterface(g, "test-boff");

    ...
}
Godhead answered 18/8, 2020 at 12:19 Comment(9)
What is the purpose of all this?Iphigeniah
@RobertHarvey. I want to test my InterfaceHandler class, which does various things. However, I don't want to instantiate the real Hardware each time CreateCommunicationInterface() is called by that main class, which is why I want to moq IProgrammer too. Not sure I am making much sense, lolGodhead
Strange. At first glance, it all looks fine. Please, change the line var moq = new Mock<IInterfaceHandler>(); to var moq = new Mock<IInterfaceHandler>(MockBehavior.Strict); and retry and tell us what happened.Appointee
Strict behavior: If it works and still return null, then it's even stranger. But I somewhat expect that it will crash with exception saying that the mock is not setup for invocation, and that means that the m.CreateCommunicationInterface you set up is not THE CreateCommunicationInterface you thought it is.Appointee
@KlausGütter, that's a typo, thanks. Not the problem though.Godhead
@quetzalcoatl, I am getting "This setup was not matched.'"Godhead
The signature of CreateCommunicationInterface seems to be IProgrammer CreateCommunicationInterface(Guid?, string). Did you try with It.IsAny<Guid?>()?Cauchy
@KlausGütter, I am not sure what you are suggesting. Number of args will be mismatching if I do that: .Returns((string value) => GetProgrammerMoq(It.IsAny<Guid?>(), value));Godhead
I meant: m.CreateCommunicationInterface(It.IsAny<Guid?>(), It.IsAny<string>()))Cauchy
A
11

Try this:

   var moq = new Mock<IInterfaceHandler>(MockBehavior.Strict);

MockBehavior.Strict: if you get NULLs from Mock, then always try MockBehavior.Strict. When some setup is not prepared, Moq by default returns NULL. But with MockBehavior.Strict, it will throw an exception. Every single attempt to call anything from the mock object, if it lacks proper setup, will throw.

If you get an exception when trying MockBehavior.Strict, then it means that the:

.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid>(), It.IsAny<string>()))

failed to catch the invocatio, so the mock returned NULL by default.

Why did it fail to catch the invocation? There are several options:

  • CreateCommunicationInterface may be overloaded and your setup matched another overload that you did not expect
  • filters (It.IsAny..) didn't match the actual arguments
  • (..)

Klaus Gütter noted in the comments about the difference of Guid and Guid?. In fact, the filter you are using is It.IsAny() while in the test you pass:

Guid? g = new Guid();

g is not an object of type Guid, it's Nullable<Guid>, hence the filter looking for any-Guid did not match. The code compiled, because the result of the expression It.IsAny<Guid>() fits Guid? wanted by the method, but still the types don't match.

If you try It.IsAny<Guid?>() it will probably match fine and return what you wanted.

moq.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid?>(), It.IsAny<string>()))
   .Returns((Guid? guid, string value) => GetProgrammerMoq(guid, value));
Appointee answered 18/8, 2020 at 13:4 Comment(5)
Thanks a lot, I did fix the Guid? thingy and added MockBehavior.Strict as you suggested and it does seem to work. Let me revert the edit in my post (I thought I had already fixed that in my code and was still breaking - thought it was not the problem :-)Godhead
What I don't get now is why if I call "moq.VerifyAll();" after my setup() in GetInterfaceHandlerMoq(), I get an error of type "This setup was not matched". Is it because all the methods need a setup?Godhead
@stackMeUp: VerifyAll is not to be called when you construct your mock object. It is not a setup method! VerifyAll is meant to be called at the very end of your test, after everything has been set up and executed. If I remember well, VerifyAll is an assertion that checks all method setups and it checks if all those setups were invoked properly at least once. It makes it easy to see if anything you have set up was ignored. Ideally, if you DO set up a thing, you want it be used, otherwise you wouldn't set it up in the first place, so if some setups are unused, then it's probably an error.Appointee
@stackMeUp: Knowing this, it is very odd to put VerifyAll in GetInterfaceHandlerMoq() that just builds some object for the test to use. I do not know why VerifyAll threw the exception you saw, maybe it's fine, maybe it is just saying that it tried to find a record of past method invocation and it failed to match any record.. I dont remember, I rarely use VerifyAll - I prefer explicit Verify for each of the methods I set up. But, please move it to the end of the test and see if the exception is gone, that will tell you/us more.Appointee
Great explanation, it makes perfect sense. I'm still discovering Moq, so it is a bit hard to understand what is best to use and how. The documentation is scarce.Godhead

© 2022 - 2024 — McMap. All rights reserved.