1

所以我试图在 ASP.NET MVC 5 中使用 Autofac Automocking,但由于某种原因我无法让它工作。

这是到目前为止的测试:

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>();
}

模拟 ApplicationUserManager 时测试失败。错误是这样的:

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]'.

尝试自动模拟 ApplicationDbContext 时会发生同样的事情,它有一个非常简单的构造函数,所以它甚至不应该有任何问题。我是 Mocking 的新手——在这种情况下我应该怎么做?

编辑:也是一个不相关的问题,也许你们知道 - 我注意到当使用先前在测试中创建的列表为 DbSet 创建 Moq 时,我必须这样做:

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());

看起来真的很不直观。有没有办法只告诉模拟拿名单?所以像:

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

或者以任何其他方式从现有列表中快速创建 DbSet Moq,而无需编写这 4 行额外的行?

4

1 回答 1

1

如果您查看MoqRegistrationHandler.cs的第 73 行,您会看到只有接口可以使用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(); 

您可以更改代码,但可能很难使其与非参数较少的依赖项一起使用。

可以将您的依赖项更改为使用接口而不是具体类吗?如果不可能和/或没有意义,您可以使用MockRepository创建非无参数组件,然后将其注入到AutoMock类中。

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;
    }
}

这不是一个完美的解决方案,但如果没有对 Autofac.Extras.Moq 项目进行大的重构,我看不到任何更简单的方法来做到这一点。

于 2015-05-19T14:23:25.810 回答