模拟单元测试在MVC 5中添加用户身份

时间:2017-05-01 05:30:52

标签: c# asp.net-mvc unit-testing asp.net-mvc-5 moq

我正在尝试为现有的MVC Web应用程序编写单元测试。因为我面临着一些问题,如用户身份和服务呼叫。

我的基本控制器:

public class BaseController : Controller
{
     public AppUser CurrentUser
    {
        get
        {
            return new AppUser(this.User as ClaimsPrincipal);
        }         
    }
}

控制器

public class NewsController : BaseController
{
    private readonly iNewsService _newsService;
    private readonly IMapper _mapper;

    public NewsController(iNewsService newsService, IMapper mapper)
    {
        _newsService = newsService;
        _mapper = mapper;
    }

    public ActionResult List(int CompanyID == 0)
    {
        var data = _newsService.List(CompanyID, CurrentUser.CompanyType);
        return View();
    }
}    

测试代码:

[TestClass]
public class NewsControllerTest
{
    [TestMethod]
    public void NewsList()
    {
        var mockNewsService = new Mock<iNewsService>();
        var mockMapper = new Mock<IMapper>();

        IList<Claim> claimCollection = new List<Claim>
        {
            new Claim("UserName", "admin"),
            new Claim("CompanyType", "true"),
        };

        var identityMock = new Mock<ClaimsIdentity>();
        identityMock.Setup(x => x.Claims).Returns(claimCollection);

        var cp = new Mock<ClaimsPrincipal>();
        cp.Setup(m => m.HasClaim(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
        cp.Setup(m => m.Identity).Returns(identityMock.Object);


        var contextMock = new Mock<HttpContextBase>();
        contextMock.Setup(ctx => ctx.User).Returns(cp.Object);

        var controllerContextMock = new Mock<ControllerContext>();            
        controllerContextMock.Setup(con => con.HttpContext).Returns(contextMock.Object);
        controllerContextMock.Setup(con => con.HttpContext.User).Returns(cp.Object);

        NewsController controller = new NewsController(mockNewsService.Object, mockMapper.Object);
        controller.ControllerContext = controllerContextMock.Object;

        // Act
        ViewResult result = controller.List(1) as ViewResult;

        // Assert
        Assert.IsNotNull(result);

    }
 }

AppUser Class:

public class AppUser : ClaimsPrincipal
{
    public AppUser(ClaimsPrincipal principal) : base(principal)
    {
    }

    public string Name
    {
        get
        {
            return this.FindFirst("UserName").Value;
        }
    }

    public string CompanyType
    {
        get
        {
            return this.FindFirst("CompanyType").Value;
        }
    }
}

INewsServices:

public interface iNewsService
{
    IEnumerable<Model.News> List(string companyType, string CompanyID);
}

注意:INewsServices是单独的库

这是我的实际代码,但它不起作用总是我的CurrentUser是空值。我无法在父控制器的CurrentUser中分配值,有时我的新闻服务也没有调用List方法。

1 个答案:

答案 0 :(得分:2)

如何模仿ClaimsPrincipal导致FindFirst返回null,当您致电Value时,它会获得NRE。无需模拟声明主体只需为测试创建新实例。

[TestClass]
public class NewsControllerTest {
    [TestMethod]
    public void _NewsList() {
        //Arrange           
        var companyId = 1;
        var companyType = "someType";
        var claims = new List<Claim>
        {
            new Claim("UserName", "admin"),
            new Claim("CompanyType", companyType),
        };
        var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));

        var contextMock = new Mock<HttpContextBase>();
        contextMock.Setup(_ => _.User).Returns(principal);

        var fakeNews = new List<News>() { new News(), new News() };
        var mockNewsService = new Mock<INewsService>();
        mockNewsService
            .Setup(_ => _.List(It.IsAny<string>(), It.IsAny<string>())
            .Returns(fakeNews);

        var controller = new NewsController(mockNewsService.Object, Mock.Of<IMapper>());
        controller.ControllerContext = new ControllerContext(contextMock.Object, new RouteData(), controller);

        // Act
        ViewResult result = controller.List(companyId) as ViewResult;

        // Assert
        Assert.IsNotNull(result);
        mockNewsService.Verify(_ => _.List(It.IsAny<string>(), It.IsAny<string>()), Times.AtLeastOnce);
    }
}

您还应考虑更新AppUser以避免NRE

public class AppUser : ClaimsPrincipal {
    public AppUser(ClaimsPrincipal principal)
        : base(principal) {
    }

    public string Name {
        get {
            return this.FindFirst("UserName")?.Value;
        }
    }

    public string CompanyType {
        get {
            return this.FindFirst("CompanyType")?.Value;
        }
    }
}