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

后端 未结 6 1666
你的背包
你的背包 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 08:42

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/unit-testing-controllers-in-web-api#testing-actions-that-return-ihttpactionresult

    Assert.IsInstanceOfType(httpActionResult, typeof(OkResult));

    0 讨论(0)
  • 2020-12-07 08:51

    Here Ok() is just a helper for the type OkResult which sets the response status to be HttpStatusCode.Ok...so you can just check if the instance of your action result is an OkResult...some examples(written in XUnit):

    // if your action returns: NotFound()
    IHttpActionResult actionResult = valuesController.Get(10);
    Assert.IsType<NotFoundResult>(actionResult);
    
    // if your action returns: Ok()
    actionResult = valuesController.Get(11);
    Assert.IsType<OkResult>(actionResult);
    
    // if your action was returning data in the body like: Ok<string>("data: 12")
    actionResult = valuesController.Get(12);
    OkNegotiatedContentResult<string> conNegResult = Assert.IsType<OkNegotiatedContentResult<string>>(actionResult);
    Assert.Equal("data: 12", conNegResult.Content);
    
    // if your action was returning data in the body like: Content<string>(HttpStatusCode.Accepted, "some updated data");
    actionResult = valuesController.Get(13);
    NegotiatedContentResult<string> negResult = Assert.IsType<NegotiatedContentResult<string>>(actionResult);
    Assert.Equal(HttpStatusCode.Accepted, negResult.StatusCode);
    Assert.Equal("some updated data", negResult.Content);
    
    0 讨论(0)
  • 2020-12-07 08:57

    Time to resurrect a dead question

    The current answers all rely on casting the response object to a known type. Unfortunately, the responses do not seem to have a useable hierarchy or implicit conversion path for this to work without intimate knowledge of the controller implementation. Consider the following:

    public class MixedCodeStandardController : ApiController {
    
        public readonly object _data = new Object();
    
        public IHttpActionResult Get() {
            return Ok(_data);
        }
    
        public IHttpActionResult Get(int id) {
            return Content(HttpStatusCode.Success, _data);
        }
    }
    

    Testing the class:

    var testController = new MixedCodeStandardController();
    
    var getResult = testController.Get();
    var posRes = getResult as OkNegotiatedContentResult<object>;
    Assert.IsType<OkNegotiatedContentResult<object>>(getResult);
    Assert.AreEqual(HttpStatusCode.Success, posRes.StatusCode);
    Assert.AreEqual(testController._data, posRes.Content);
    
    var idResult = testController.Get(1);
    var oddRes = getResult as OkNegotiatedContentResult<object>; // oddRes is null
    Assert.IsType<OkNegotiatedContentResult<object>>(idResult); // throws failed assertion
    Assert.AreEqual(HttpStatusCode.Success, oddRes.StatusCode); // throws for null ref
    Assert.AreEqual(testController._data, oddRes.Content); // throws for null ref
    

    From outside the black box, the response stream is essentially the same. The test must know how the controller implemented the return call to test it in this way.

    Instead, use the HttpResponseMessage object from the IHttpActionResult returned. This ensures the test can be consistent, even when the controller code may not be:

    var testController = new MixedCodeStandardController();
    
    var getResult = testController.Get();
    var getResponse = getResult.ExecuteAsync(CancellationToken.None).Result;
    Assert.IsTrue(getResponse.IsSuccessStatusCode);
    Assert.AreEqual(HttpStatusCode.Success, getResponse.StatusCode);
    
    var idResult = testController.Get(1);
    var idResponse = idResult.ExecuteAsync(CancellationToken.None).Result;
    Assert.IsTrue(idResponse.IsSuccessStatusCode);
    Assert.AreEqual(HttpStatusCode.Success, idResponse.StatusCode);
    
    0 讨论(0)
  • 2020-12-07 09:03

    If IHttpActionResult contain a JSON object, e.g. {"token":"A"}, we can use the following code.

            var result = usercontroller.GetLogin("user", "password");
            Assert.IsInstanceOfType(result, typeof(OkNegotiatedContentResult<Dictionary<string,string>>));
            var content = result as OkNegotiatedContentResult<Dictionary<string, string> >;
            Assert.AreEqual("A", content.Content["token"]);
    
    0 讨论(0)
  • 2020-12-07 09:04

    This is the accepted answer by Kiran Challa, adapted for NUnit;

    var valuesController = controller;
    // if your action returns: NotFound()
    IHttpActionResult actionResult = valuesController.Get(10);
    var notFoundRes = actionResult as NotFoundResult;
    Assert.IsNotNull(notFoundRes);
    
    // if your action returns: Ok()
    actionResult = valuesController.Get(11);
    var posRes = actionResult as OkResult;
    Assert.IsNotNull(posRes);
    
    // if your action was returning data in the body like: Ok<string>("data: 12")
    actionResult = valuesController.Get(12);
    var conNegResult = actionResult as OkNegotiatedContentResult<string>;
    Assert.IsNotNull(conNegResult);
    Assert.AreEqual("data: 12", conNegResult.Content);
    
    // if your action was returning data in the body like: Content<string>(HttpStatusCode.Accepted, "some updated data");
    actionResult = valuesController.Get(13);
    var negResult = actionResult as NegotiatedContentResult<string>;
    Assert.IsNotNull(negResult);
    Assert.AreEqual(HttpStatusCode.Accepted, negResult.StatusCode);
    Assert.AreEqual("some updated data", negResult.Content);
    
    0 讨论(0)
  • 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<ApplicationUserManager>();
    
        [Route("api/account"), HttpGet]
        public async Task<IHttpActionResult> 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<string> 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<string>
            {
                "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<ApplicationSignInManager> GetMockedApplicationSignInManager(Mock<ApplicationUserManager> appUserMgrMock)
        {
            var authMgr = new Mock<Microsoft.Owin.Security.IAuthenticationManager>();
            var appSignInMgr = new Mock<ApplicationSignInManager>(appUserMgrMock.Object, authMgr.Object);
    
            return appSignInMgr;
        }
    
        private Mock<ApplicationUserManager> GetMockedApplicationUserManager()
        {
            var userStore = new Mock<IUserStore<User>>();
            var appUserMgr = new Mock<ApplicationUserManager>(userStore.Object);
            appUserMgr.Setup(aum => aum.FindByIdAsync(It.IsAny<string>())).ReturnsAsync(CurrentUser);
            appUserMgr.Setup(aum => aum.GetRolesAsync(It.IsAny<string>())).ReturnsAsync(Roles);
    
            return appUserMgr;
        }
    
        private static Mock<ApplicationDbContext> GetMockedApplicationDbContext()
        {
            var dbContext = new Mock<ApplicationDbContext>();
            dbContext.Setup(dbc => dbc.Users).Returns(MockedUsersDbSet);
    
            return dbContext;
        }
    
        private static IDbSet<User> MockedUsersDbSet()
        {
            var users = new List<User>
            {
                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<DbSet<User>>();
            usersMock.As<IQueryable<User>>().Setup(m => m.Provider).Returns(users.Provider);
            usersMock.As<IQueryable<User>>().Setup(m => m.Expression).Returns(users.Expression);
            usersMock.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(users.ElementType);
            usersMock.As<IQueryable<User>>().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:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="Castle.Core" version="4.3.1" targetFramework="net472" />
      <package id="EntityFramework" version="6.2.0" targetFramework="net472" />
      <package id="Microsoft.AspNet.Identity.Core" version="2.2.2" targetFramework="net472" />
      <package id="Microsoft.AspNet.Identity.EntityFramework" version="2.2.2" targetFramework="net472" />
      <package id="Microsoft.AspNet.Identity.Owin" version="2.2.2" targetFramework="net472" />
      <package id="Microsoft.AspNet.WebApi.Client" version="5.2.7" targetFramework="net472" />
      <package id="Microsoft.AspNet.WebApi.Core" version="5.2.7" targetFramework="net472" />
      <package id="Microsoft.AspNet.WebApi.Owin" version="5.2.7" targetFramework="net472" />
      <package id="Microsoft.Owin" version="4.0.1" targetFramework="net472" />
      <package id="Microsoft.Owin.Host.SystemWeb" version="4.0.1" targetFramework="net472" />
      <package id="Microsoft.Owin.Security" version="4.0.1" targetFramework="net472" />
      <package id="Microsoft.Owin.Security.Cookies" version="4.0.1" targetFramework="net472" />
      <package id="Microsoft.Owin.Security.OAuth" version="4.0.1" targetFramework="net472" />
      <package id="Moq" version="4.10.1" targetFramework="net472" />
      <package id="Newtonsoft.Json" version="12.0.1" targetFramework="net472" />
      <package id="NUnit" version="3.11.0" targetFramework="net472" />
      <package id="Owin" version="1.0" targetFramework="net472" />
      <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net472" />
      <package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net472" />
    </packages>
    

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

    Happy testing!

    0 讨论(0)
提交回复
热议问题