Fabis
Fabis

Reputation: 2062

Autofac Automocking in ASP.NET MVC

So I'm trying to use Autofac Automocking in ASP.NET MVC 5, but for some reason I can't get it to work.

Here's the test so far:

using (var mock = AutoMock.GetLoose())
{
            const string mainUserID = "MainUserID";
            const string otherUserID = "OtherUserID";
            ApplicationUser user = new ApplicationUser()
            {
                Id = mainUserID,
                UserName = "TestUser"
            };

            var dataProvider = mock.Mock<IDataProtectionProvider>();
            dataProvider.DefaultValue = DefaultValue.Mock;

            var userManagerMock = mock.Mock<ApplicationUserManager>();
}

The test fails when mocking the ApplicationUserManager. The error is this:

Result StackTrace:  
at Autofac.Extras.Moq.AutoMock.Mock[T](Parameter[] parameters)
at AwenterWeb_NUnit.AccountControllerTest.<Deactivate_User>d__0.MoveNext() in C:\Users\Fabis\Documents\Docs\Kvalifikācijas darbs 2015\AwenterWeb\AwenterWeb-NUnit\AccountControllerTest.cs:line 51
at NUnit.Framework.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
at NUnit.Core.NUnitAsyncTestMethod.RunTestMethod()
Result Message: System.InvalidCastException : Unable to cast object of type 'AwenterWeb.ApplicationUserManager' to type 'Moq.IMocked`1[AwenterWeb.ApplicationUserManager]'.

The same thing happens when trying to automock the ApplicationDbContext and it has a very simple constructor, so there shouldn't even be any issues with it. I'm new to Mocking - what should I do in this scenario?

Edit: Also kind of an unrelated question, maybe you guys know - I've noticed that when creating a Moq for a DbSet using a list created previously in the test, I have to do this:

var dbSetMock = new Mock<IDbSet<DbEntity>>();
            dbSetMock.Setup(m => m.Provider).Returns(data.Provider);
            dbSetMock.Setup(m => m.Expression).Returns(data.Expression);
            dbSetMock.Setup(m => m.ElementType).Returns(data.ElementType);
            dbSetMock.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

It seems really unintuitive. Is there a way to just tell the mock to take the list? So something like:

dbSetMock.Setup(m => m).Returns(data);

Or any other way to create a DbSet Moq from an existing list quickly without having to write those 4 extra lines?

Upvotes: 1

Views: 2037

Answers (1)

Cyril Durand
Cyril Durand

Reputation: 16192

If you look at ligne 73 of MoqRegistrationHandler.cs you can see that only interface is moqable using Autofac.Extras.Moq

var typedService = service as TypedService; 
if (typedService == null || 
    !typedService.ServiceType.IsInterface || 
    typedService.ServiceType.IsGenericType && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IEnumerable<>) || 
    typedService.ServiceType.IsArray || 
    typeof(IStartable).IsAssignableFrom(typedService.ServiceType)) 
    return Enumerable.Empty<IComponentRegistration>(); 


var rb = RegistrationBuilder.ForDelegate((c, p) => CreateMock(c, typedService)) 
    .As(service) 
    .InstancePerLifetimeScope(); 

You can change the code but it may be quite difficult to make it works with non parameter less dependency.

Can your dependencies be changed to use an interface instead of a concrete class ? if it is not possible and/or if it doesn't make sense, you can use the MockRepository to create your non parameter-less component and then inject it on the AutoMock class.

class Program
{
    static void Main(string[] args)
    {
        using (var mock = AutoMock.GetLoose())
        {
            /// configure your non interface component with constructor parameters
            /// if foo need more complex parameters you can get them 
            /// using mock.Mock<T>().Object
            var fooMock = mock.MockRepository.Create<Foo>((String)null);
            fooMock.SetupGet(f => f.Value).Returns("test");

            // insert your instance into the container
            mock.Provide<Foo>(fooMock.Object); 


            var bar = mock.Create<Bar>();
            Console.WriteLine(bar.GetValue());
        }
    }
}

public class Foo
{
    public Foo(String value)
    {
        this._value = value;
    }

    private readonly String _value;

    public virtual String Value
    {
        get
        {
            return this._value;
        }
    }
}

public interface IBar
{
    String GetValue();
}
public class Bar : IBar
{
    public Bar(Foo foo)
    {
        this._foo = foo;
    }

    private readonly Foo _foo;

    public String GetValue()
    {
        return this._foo.Value;
    }
}

It is not a perfect solution but without big refactoring of the Autofac.Extras.Moq project I can't see any simpler way to do it.

Upvotes: 1

Related Questions