首页 > 解决方案 > 如何通过 Moq、ASP .NET MVC 进行模块测试

问题描述

我有一个方法,它返回给我做ajax操作的返回视图,我如何测试它来检查正确的数据返回与否?

[HttpPost]
    public ActionResult ViewPlayers(string teamName)
    {
        if (teamName.Contains("Все игроки"))
        {
            return PartialView(playerRepository.Players.ToList());
        }
        else
        {

            if (teamName != string.Empty)// send team-logo image path
            {
                Team findingTeam = teamRepository.Teams.First(t => t.Name.Contains(teamName));
                ViewBag.TeamLogoPath = findingTeam.Path;
            }
            List<Player> allTeam = playerRepository.Players.Where(t => t.Team.Name.Contains(teamName)).ToList();

            return PartialView(allTeam);
        }
    }

在此处输入图像描述

当我在下拉列表中选择一个团队时,我会string name在那之后发布,

1) 如果"Все игроки"我和所有玩家一起坐一张桌子

2) 否则我会在这支球队中找到该球员。

签到测试如何返回所有球员或球队的某些球员?

标签: c#asp.net-mvcmoq

解决方案


您是否尝试过简单地调用该方法并检查响应?MVC 框架的好处是你不需要整个框架来使用控制器。堆栈中的其他组件将获取 ActionResult 并将其呈现为 HTML。

PartialView返回一个PartialViewResult,其Model属性包含传递给 的模型PartialView

您应该能够编写一个创建控制器、调用操作并检查结果模型的单元测试,例如:

[Test]
public void viewPlayers_Returns_One()
{
    var myController=new MyController(...);
    var resultView=(PartialViewResult)myController.ViewPlayers("SomeName");
    var players= (List<Player>)resultView.Model;

    Assert.That(players, Has.Exactly(1).Items);
}

嘲笑

现在棘手的部分是注入存储库。鉴于类似于 LINQ 的语法,我假设playerRepository是通过构造函数注入的 EF 存储库或通过属性公开 EF 实体的存储库对象Players

模拟 EF 显示在多个SO 问题EF 文档中。

EF Core通过其内存数据库提供程序使模拟变得更加容易。EF Core 不依赖于 .NET 运行时,这意味着它也可以在完整框架应用程序中使用。文档中使用 InMemory进行测试包含更详细的示例。还有一个示例显示了使用 SQLite 进行更高级的场景

懒惰,我假设存储库是一个实现接口的类:

public interface IPlayersRepository
{
    public IQueryable<Player> Players {get;}
}

创建一个接受列表或数组或播放器并通过以下方式公开它的模拟类很容易Players

class MockPlayers
{
    public IQueryable<Player> Players {get; private set;}

    public MockPlayers(Player[] players)
    {
        Players=players.AsQueryable();
    }
}

这很简单,甚至不需要模拟框架。之后,测试变为:

[Test]
public void viewPlayers_Returns_One()
{
    var players=new[]{new Player{Name="SomeName"},new Player{Name="Other Name"}};
    var mockRepo=new MockPlayers(players);
    var myController=new MyController(mockRepo);
    ...
}

Moq 对于更复杂的界面很有用。使用起订量,您可能会写:

var players = new[]{...};
var mockRepo=new Mock<IPlayersRepository>();
mockRepo.SetupGet(x => x.Players).Returns(players.AsQueryable());

EF 核心

如果您注入一个 EF Core 上下文,例如 PlayersContext,您可以将其配置为使用内存提供程序:

//setup
var options = new DbContextOptionsBuilder<PlayersContext>()
    .UseInMemoryDatabase(databaseName: "Players Test")
    .Options;

using(var ctx = new PlayersContext(options))
{
    ctx.Players.Add(new Player{...});
    ctx.SaveChanges();
}

//Execute
using(var ctx = new PlayersContext(options))
{
    var myController=new MyController(ctx);
    ...
}

“经典”英孚

事情有点复杂,需要模拟 DbSet 及其一些方法。调整文档的查询场景如下所示:

//Setup 
...
var data = players.AsQueryable();

var mockSet = new Mock<DbSet<Player>>();
mockSet.As<IQueryable<Player>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Player>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Player>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Player>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

var mockContext = new Mock<PlayerContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);

//Execute
var myController=new MyController(mockContext.Object);
...

我有没有提到 EF Core 可以在完整框架应用程序中使用?


推荐阅读