Sam
Sam

Reputation: 15771

Setup Moq To Return Multiple Values

I am new to Moq and I want to have a test like this:

[Fact]
        public void IsClientExternalForWebShouldReturnFalse()
        {
            // Arrange
            var request = new Mock<HttpRequestBase>();
            request.Setup(r => r.UserHostAddress).Returns(new Queue<string>(new[] { "127.0.0.1", "10.1.10.1" }).Dequeue);

            var context = new Mock<HttpContextBase>();
            context.SetupGet(c => c.Request).Returns(request.Object);

            var service = new EnvironmentService(context.Object, null);

            // Act / Assert
            service.IsClientExternal.Should().BeFalse();
            service.IsClientExternal.Should().BeFalse();
        }

It throws this:

------ Test started: Assembly: SAIF.Services.Tests.dll ------

Test 'SAIF.Services.Tests.EnvironmentServiceTests.IsClientExternalForWebShouldReturnFalse' failed: System.InvalidOperationException : Queue empty.
    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
    at System.Collections.Generic.Queue`1.Dequeue()   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
    at System.Collections.Generic.Queue`1.Dequeue()   at Moq.Extensions.InvokePreserveStack(Delegate del, Object[] args)
    at Moq.MethodCallReturn`2.Execute(ICallContext call)
    at Moq.Interceptor.Intercept(ICallContext invocation)
    at Moq.Proxy.CastleProxyFactory.Interceptor.Intercept(IInvocation invocation)
    at Castle.DynamicProxy.AbstractInvocation.Proceed()
    at Castle.Proxies.HttpRequestBaseProxy.get_UserHostAddress()
    Services\EnvironmentService.cs(54,0): at SAIF.Services.EnvironmentService.<get_IsClientExternal>b__3(String ip)
    at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
    Services\EnvironmentService.cs(54,0): at SAIF.Services.EnvironmentService.get_IsClientExternal()
    EnvironmentServiceTests.cs(25,0): at SAIF.Services.Tests.EnvironmentServiceTests.IsClientExternalForWebShouldReturnFalse()

0 passed, 1 failed, 0 skipped, took 0.69 seconds (xUnit.net 1.9.1 build 1600).

The actual code under test:

public bool IsClientExternal
        {
            get { return IsWeb && !internalAddressRoots.Any(ip => httpContext.Request.UserHostAddress.StartsWith(ip)); }
        }

But if I only call to the service once it works, but the service only gets the first value.

Also if I debug and mouse over the httpContext.Request.UserHostAddress, the value changes.

Any ideas?

Edit #1

So I found the issue I think. This issue is that the actual code under test loops through all the ip's and doing that calls the UserHostAddress. I just really need to figure out how to supply an array of strings into the Returns() function of the mock.

Upvotes: 0

Views: 2545

Answers (1)

Bartosz
Bartosz

Reputation: 3358

Your UserHostAddress property might be called few times in call to Any, and we do not know how many times it's gonna be called (depends on content of internalAddressRoots). So by the time you get to the second service call, your queue might be empty.

If you really want to test twice, maybe you should setup twice. Subsequent setups override previous ones:

    public void IsClientExternalForWebShouldReturnFalse()
    {
        // Arrange
        var request = new Mock<HttpRequestBase>();
        request.Setup(r => r.UserHostAddress).Returns("127.0.0.1");

        var context = new Mock<HttpContextBase>();
        context.SetupGet(c => c.Request).Returns(request.Object);

        var service = new EnvironmentService(context.Object, null);

        // Act / Assert
        service.IsClientExternal.Should().BeFalse();

        request.Setup(r => r.UserHostAddress).Returns("10.1.10.1");
        service.IsClientExternal.Should().BeFalse();
    }

As you see, we're mixing setup with the assert part, which doesn't look that good, but I'm not sure about your motives, so here's one possible solution.

Upvotes: 2

Related Questions