Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking generic methods in Moq without specifying T

I have an interface with a method as follows:

public interface IRepo {     IA<T> Reserve<T>(); } 

I would like to mock the class that contains this method without having to specify Setup methods for every type it could be used for. Ideally, I'd just like it to return a new mock<T>.Object.

How do I achieve this?

It seems my explanation was unclear. Here's an example - this is possible right now, when I specify the T (here, string):

[TestMethod] public void ExampleTest() {     var mock = new Mock<IRepo>();     mock.Setup(pa => pa.Reserve<string>()).Returns(new Mock<IA<string>>().Object); } 

What I would like to achieve is something like this:

[TestMethod] public void ExampleTest() {     var mock = new Mock<IRepo>();     mock.Setup(pa => pa.Reserve<T>()).Returns(new Mock<IA<T>>().Object);     // of course T doesn't exist here. But I would like to specify all types     // without having to repeat the .Setup(...) line for each of them. } 

Some methods of the object under test might call reserve for three or four different types. If I have to setup all the types I have to write a lot of setup code for every test. But in a single test, I am not concerned with all of them, I simply need non-null mocked objects except for the one that I am actually testing (and for which I gladly write a more complex setup).

like image 970
Wilbert Avatar asked Nov 19 '13 13:11

Wilbert


People also ask

What can be mocked with Moq?

Unit testing is a powerful way to ensure that your code works as intended. It's a great way to combat the common “works on my machine” problem. Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation.

Can you mock a class with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.

How do you call an original method in mock?

Set CallBase to true on your mock. This will call the original virtual methods or properties if they exist, and haven't been set up to return a canned value.

How does Moq mock work?

Mock objects allow you to mimic the behavior of classes and interfaces, letting the code in the test interact with them as if they were real. This isolates the code you're testing, ensuring that it works on its own and that no other code will make the tests fail.


2 Answers

In Moq 4.13 they introduced the It.IsAnyType type which you can using to mock generic methods. E.g.

public interface IFoo {     bool M1<T>();     bool M2<T>(T arg); }  var mock = new Mock<IFoo>(); // matches any type argument: mock.Setup(m => m.M1<It.IsAnyType>()).Returns(true);  // matches only type arguments that are subtypes of / implement T: mock.Setup(m => m.M1<It.IsSubtype<T>>()).Returns(true);  // use of type matchers is allowed in the argument list: mock.Setup(m => m.M2(It.IsAny<It.IsAnyType>())).Returns(true); mock.Setup(m => m.M2(It.IsAny<It.IsSubtype<T>>())).Returns(true); 
like image 198
John Gamble Avatar answered Sep 20 '22 06:09

John Gamble


Simply do this:

[TestMethod] public void ExampleTest() {   var mock = new Mock<IRepo> { DefaultValue = DefaultValue.Mock, };   // no setups needed!    ... } 

Since your mock does not have behavior Strict, it will be happy with calls that you haven't even set up. In that case a "default" is simply returned. Then

DefaultValue.Mock 

ensures that this "default" is a new Mock<> of appropriate type, instead of just a null reference.

The limitation here is that you cannot control (e.g. make special setups on) the individual "sub-mocks" that are returned.

like image 27
Jeppe Stig Nielsen Avatar answered Sep 24 '22 06:09

Jeppe Stig Nielsen