6

I'm trying to mock this method

Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc)

like this

iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>())).ReturnsAsync(new { isPair = false });

The method to test doing the call passing an anonymous type to the generic parameter like this

instance.GetResultAsync(u => new {isPair = u == "something" }) //dont look at the function return because as generic could have diferent implementations in many case

Moq never matches my GetResultAsync method with the parameters sent.

I'm using Moq 4

Nkosi
  • 235,767
  • 35
  • 427
  • 472
RJardines
  • 850
  • 9
  • 18
  • Not sure... but using Setup for this purpose may not be correct. If you have created the anonymous object in your unit test `var testObject = { whatevs }` you can skip the setup and verify it at the end `iMock.Verify(x => x.GetResultAsync(testObject))`. I believe this should work as expected. –  Sep 09 '16 at 17:10
  • 2
    How do you want the mock to behave? you are not specifying anything. – Yacoub Massad Sep 09 '16 at 17:10
  • Will and Yacoub thanks for your interest. I updated my question with the return of this mocked method. @Will, I need to mock the behavior of the method because it's going to be used inside another method I'm testing – RJardines Sep 09 '16 at 17:50
  • 1
    As a heads up, this won't work without some extra effort. You can't create an anonymous object, and use it in another project. I ran into [an issue doing the same thing last year](http://stackoverflow.com/q/29755234/1195056). – krillgar Sep 09 '16 at 17:58
  • One possible solution is to tell the mocking framework to return an object that you create via reflection of type `TResult` and that you have set its `isPair` property via reflection to `false`. I am not sure if this can be done with Moq, but it can be done with FakeItEasy. It is a complex solution but it should work. Let me know if it is in option for you to use FakeItEasy and I will post a solution. – Yacoub Massad Sep 09 '16 at 22:05
  • 1
    I posted this as an issue over at Moq's issue tracker: [`moq/moq4#552`](https://github.com/moq/moq4/issues/552). Matching objects of anonymous types looks like something that *should* work; I'll look into it. – stakx - no longer contributing Dec 12 '17 at 08:54

1 Answers1

5

The anonymous type is going to cause you problems. You need a concrete type for this to work.

The following example worked when I changed

instance.GetResultAsync(u => new {isPair = u == "something" })

to

instance.GetResultAsync(u => (object) new {isPair = u == "something" })

Moq is unable to match the anonymous type and that is why you get null when called.

[TestClass]
public class MoqUnitTest {
    [TestMethod]
    public async Task Moq_Function_With_Anonymous_Type() {
        //Arrange
        var expected = new { isPair = false };

        var iMock = new Mock<IService>();
        iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>()))
            .ReturnsAsync(expected);

        var consumer = new Consumer(iMock.Object);

        //Act   
        var actual = await consumer.Act();

        //Assert
        Assert.AreEqual(expected, actual);
    }

    public interface IService {
        Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
    }

    public class Consumer {
        private IService instance;

        public Consumer(IService service) {
            this.instance = service;
        }

        public async Task<object> Act() {
            var result = await instance.GetResultAsync(u => (object)new { isPair = u == "something" });
            return result;
        }
    }
}

if the code calling the GetResultAsync is dependent on using the anonymous type then what you are trying to do with your test wont work with your current setup. You would probably need to provide a concrete type to the method.

[TestClass]
public class MoqUnitTest {

    [TestMethod]
    public async Task Moq_Function_With_Concrete_Type() {
        //Arrange
        var expected = new ConcreteType { isPair = false };

        var iMock = new Mock<IService>();
        iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, ConcreteType>>()))
            .ReturnsAsync(expected);

        var sut = new SystemUnderTest(iMock.Object);

        //Act   
        var actual = await sut.MethodUnderTest();

        //Assert
        Assert.AreEqual(expected, actual);
    }

    class ConcreteType {
        public bool isPair { get; set; }
    }

    public interface IService {
        Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
    }

    public class SystemUnderTest {
        private IService instance;

        public SystemUnderTest(IService service) {
            this.instance = service;
        }

        public async Task<object> MethodUnderTest() {
            var result = await instance.GetResultAsync(u => new ConcreteType { isPair = u == "something" });
            return result;
        }
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472