How do I unit test web api action method when it returns IHttpActionResult?

后端 未结 6 1701
你的背包
你的背包 2020-12-07 08:03

Let\'s assume this is my action method

public IHttpActionResult Get(int id)
{
    var status = GetSomething(id);
    if (status)
    {
        return Ok();
          


        
6条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-07 09:06

    After a few hours of research and trying, I finally figured out how to fully test my Web API 2 methods that return IHttpActionResult and use the OWIN middleware and the default implementation of ASP.NET Identity.

    I will be testing the Get() method on the following ApiController:

    public class AccountController : ApiController
    {
        private ApplicationUserManager _userManager;
        public ApplicationUserManager UserManager => _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager();
    
        [Route("api/account"), HttpGet]
        public async Task Get()
        {
            var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
            if (user == null)
            {
                ModelState.AddModelError(ModelStateConstants.Errors, "Account not found! Try logging out and in again.");
                return BadRequest(ModelState);
            }
    
            var roles = await UserManager.GetRolesAsync(user.Id);
    
            var accountModel = new AccountViewModel
            {
                FullName = user.FullName,
                Email = user.Email,
                Phone = user.PhoneNumber,
                Organization = user.Organization.Name,
                Role = string.Join(", ", roles)
            };
    
            return Ok(accountModel);
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_userManager != null)
                {
                    _userManager.Dispose();
                    _userManager = null;
                }
            }
    
            base.Dispose(disposing);
        }
    }
    

    Start with a base class that all test classes will inherit from:

    public class BaseTest
    {
        protected static User CurrentUser;
        protected static IList Roles;
    
        public BaseTest()
        {
            var email = "unit@test.com";
    
            CurrentUser = new User
            {
                FullName = "Unit Tester",
                Email = email,
                UserName = email,
                PhoneNumber = "123456",
                Organization = new Organization
                {
                    Name = "Test Organization"
                }
            };
    
            Roles = new List
            {
                "Administrator"
            };
        }
    
        protected void InitializeApiController(ApiController apiController)
        {
            //Init fake controller Http and Identity data
            var config = new HttpConfiguration();
            var request = new HttpRequestMessage();
            var routeData = new HttpRouteData(new HttpRoute(""));
            apiController.ControllerContext = new HttpControllerContext(config, routeData, request)
            {
                Configuration = config
            };
    
            apiController.User = new GenericPrincipal(new GenericIdentity(""), new[] { "" });
    
            //Initialize Mocks
            var appUserMgrMock = GetMockedApplicationUserManager();
            var appSignInMgr = GetMockedApplicationSignInManager(appUserMgrMock);
            var appDbContext = GetMockedApplicationDbContext();
    
            //Configure HttpContext.Current.GetOwinContext to return mocks
            var owin = new OwinContext();
            owin.Set(appUserMgrMock.Object);
            owin.Set(appSignInMgr.Object);
            owin.Set(appDbContext.Object);
    
            HttpContext.Current = new HttpContext(new HttpRequest(null, "http://test.com", null), new HttpResponse(null));
            HttpContext.Current.Items["owin.Environment"] = owin.Environment;
        }
    
        private static Mock GetMockedApplicationSignInManager(Mock appUserMgrMock)
        {
            var authMgr = new Mock();
            var appSignInMgr = new Mock(appUserMgrMock.Object, authMgr.Object);
    
            return appSignInMgr;
        }
    
        private Mock GetMockedApplicationUserManager()
        {
            var userStore = new Mock>();
            var appUserMgr = new Mock(userStore.Object);
            appUserMgr.Setup(aum => aum.FindByIdAsync(It.IsAny())).ReturnsAsync(CurrentUser);
            appUserMgr.Setup(aum => aum.GetRolesAsync(It.IsAny())).ReturnsAsync(Roles);
    
            return appUserMgr;
        }
    
        private static Mock GetMockedApplicationDbContext()
        {
            var dbContext = new Mock();
            dbContext.Setup(dbc => dbc.Users).Returns(MockedUsersDbSet);
    
            return dbContext;
        }
    
        private static IDbSet MockedUsersDbSet()
        {
            var users = new List
            {
                CurrentUser,
                new User
                {
                    FullName = "Testguy #1",
                    Email = "test@guy1.com",
                    UserName = "test@guy1.com",
                    PhoneNumber = "123456",
                    Organization = new Organization
                    {
                        Name = "Test Organization"
                    }
                }
            }.AsQueryable();
    
            var usersMock = new Mock>();
            usersMock.As>().Setup(m => m.Provider).Returns(users.Provider);
            usersMock.As>().Setup(m => m.Expression).Returns(users.Expression);
            usersMock.As>().Setup(m => m.ElementType).Returns(users.ElementType);
            usersMock.As>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator);
    
            return usersMock.Object;
        }
    }
    

    The InitializeApiController method contains the meat and potatoes.

    Now we can write our tests for AccountController:

    public class AccountControllerTests : BaseTest
    {
        private readonly AccountController _accountController;
    
        public AccountControllerTests()
        {
            _accountController = new AccountController();
            InitializeApiController(_accountController);
        }
    
        [Test]
        public async Task GetShouldReturnOk()
        {
            var result = await _accountController.Get();
            var response = await result.ExecuteAsync(CancellationToken.None);
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        }
    }
    

    For everything to work, you'll need to install a bunch of Microsoft.OWIN.* and Microsoft.AspNet.* packages, I'll paste my packages.config here:

    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    

    The test is very simple, but demonstrates that everything works :-)

    Happy testing!

提交回复
热议问题