MVC 5异步任务<ActionResult>索引视图的单元测试

bla*_*ner 9 c# asp.net-mvc unit-testing asp.net-mvc-5

我是单元测试的新手.不知道该怎么做我的测试,我继续在ClientController上用Index视图启动一个.我在编写索引视图的测试方法时遇到问题,该视图被声明为异步任务.我想要做的是确保此操作返回数据.不确定这个测试有多重要,但我想学习在MVC 5中编写测试的方法.似乎无法为异步视图编写测试.

ManagementService类

public IQueryable<Client> GetAllClients(bool includeDeleted = false)
    {
        var query = context.Clients.AsQueryable();
        if (!includeDeleted) query = query.Where(c => !c.IsDeleted);
        return query;
    }
Run Code Online (Sandbox Code Playgroud)

客户控制器

public ClientController(IApplicationContext appContext, IManagementService adminService, IDomainService domainService)
    {
        this.adminService = adminService;
        this.appContext = appContext;
        this.domainService = domainService;
    }
public async Task<ActionResult> Index()
    {
        var clients = adminService.GetAllClients();
        return View(await clients.ToListAsync());
    }
Run Code Online (Sandbox Code Playgroud)

//测试

[TestMethod]
    public void Index()
    {
        //Arrange
        var mgmtmockContext = new Mock<IManagementService>();
        List<Client> db = new List<Client>();
        db.Add(new Client { ClientId = 1, Name = "Test Client 1" });
        db.Add(new Client { ClientId = 2, Name = "Test Client 2" });
        mgmtmockContext
            .Setup(m => m.GetAllClients(false))
            .Returns(() => { return db.AsQueryable(); });

        //Act
        ClientController controller = new ClientController(null, mgmtmockContext.Object, null);

        var result = controller.Index() as Task<ActionResult>;

        //Assert
        Assert.IsNotNull(result);
    }
Run Code Online (Sandbox Code Playgroud)

在我创建控制器的实例后,我不知道如何将模拟对象传递给Index(),因此我可以测试此视图返回数据.

我想要Assert的是Assert.AreEqual(2,result.Count())所以我可以运行这个测试.

Ken*_*eth 6

为了从结果中获取列表,您需要做两件事:

  1. 从任务中获取实际结果
  2. 从动作结果中获取模型

这是你如何做到这一点:

    var result = controller.Index() as Task<ViewResult>;

   // Get the actual result from the task
    var viewresult = result.Result;

    // Get the model from the viewresult and cast it to the correct type 
    // (notice in the first line I changed ActionResult to ViewResult to make sure we can access the model.
    var model = (List<Client>)(viewresult.Model);

    //Assert
    Assert.IsNotNull(result);
    Assert.AreEqual(2, model.Count);
Run Code Online (Sandbox Code Playgroud)


Ale*_*kov 5

您可以controller.Index().Result将测试更改为async

[TestMethod]
 public async Task Index()
{ 
  ....
   var result = await controller.Index();
   Assert.IsNotNull(result);
   var model = (List<Client>)((ViewResult)result).Model;
   Assert.AreEqual(2, model.Count())
 }
Run Code Online (Sandbox Code Playgroud)


bla*_*ner 4

想通了这一点。测试异步并不容易。从asp.net 获取MockingHelper 类。

public static class MockingHelper
{
    public static Mock<DbSet<TEntity>> AsMockDbSet<TEntity>(this IQueryable<TEntity> data)
        where TEntity : class
    {
        var mockSet = new Mock<DbSet<TEntity>>();
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(data.Provider));
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
        mockSet.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
        return mockSet;
    }

    public static Mock<DbSet<TEntity>> AsMockDbSet<TEntity>(this IEnumerable<TEntity> data)
        where TEntity : class
    {
        return data.AsQueryable().AsMockDbSet();
    }
}
Run Code Online (Sandbox Code Playgroud)

还有 AsyncTestingClasses。

internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
    private readonly IQueryProvider _inner;

    internal TestDbAsyncQueryProvider(IQueryProvider inner)
    {
        _inner = inner;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return new TestDbAsyncEnumerable<TEntity>(expression);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new TestDbAsyncEnumerable<TElement>(expression);
    }

    public object Execute(Expression expression)
    {
        return _inner.Execute(expression);
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return _inner.Execute<TResult>(expression);
    }

    public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute(expression));
    }

    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute<TResult>(expression));
    }
}

internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
    public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
        : base(enumerable)
    { }

    public TestDbAsyncEnumerable(Expression expression)
        : base(expression)
    { }

    public IDbAsyncEnumerator<T> GetAsyncEnumerator()
    {
        return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
    }

    IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
    {
        return GetAsyncEnumerator();
    }

    IQueryProvider IQueryable.Provider
    {
        get { return new TestDbAsyncQueryProvider<T>(this); }
    }
}

internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
    private readonly IEnumerator<T> _inner;

    public TestDbAsyncEnumerator(IEnumerator<T> inner)
    {
        _inner = inner;
    }

    public void Dispose()
    {
        _inner.Dispose();
    }

    public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(_inner.MoveNext());
    }

    public T Current
    {
        get { return _inner.Current; }
    }

    object IDbAsyncEnumerator.Current
    {
        get { return Current; }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后更改我的测试方法如下。

public void Default_Index_Returns_Data()
    {
        //Arrange
        var appmockContext = new Mock<IApplicationContext>();
        var mgmtmockContext = new Mock<IManagementService>();
        var domainmockContext = new Mock<IDomainService>();
        var agency1 = new Agency() { Name = "Test Agency" };
        List<Client> db = new List<Client>();

        db.Add(new Client { ClientId = 1, Name = "Test Client 1", AgencyId = 1 });
        db.Add(new Client { ClientId = 2, Name = "Test Client 2", AgencyId = 1 });

        //Act
        mgmtmockContext
            .Setup(m => m.GetAllClients(It.IsAny<bool>()))
            .Returns(() => { return db.AsMockDbSet().Object; });
        ClientController controller = new ClientController(appmockContext.Object, mgmtmockContext.Object, domainmockContext.Object);

        var resultTask = controller.Index();
        resultTask.Wait();
        var result = resultTask.Result;
        var model = (List<Client>)((ViewResult)result).Model;

        //Assert
        Assert.IsNotNull(result);
        Assert.AreEqual(2, model.Count());
    }
Run Code Online (Sandbox Code Playgroud)

这有效。感谢阿列克谢和肯尼斯的帮助。我希望这对大家有帮助。