Search code examples
c#unit-testingasp.net-core-webapiasp.net-core-mvc-2.0

How to test and call my api in external DLL


Using ASP.NET Core 2.0 for 1st time.

I have a web project which references a DLL that i have created.

In this DLL is a simple method...

namespace InformedWorkerApi
{
    [Route("api/[controller]")]
    public class RegistrationController: Controller
    {
        private readonly IAccountRepository _accountRepository;
        public RegistrationController(IAccountRepository accountRepository)
        {
            _accountRepository = accountRepository;
        }

        [HttpPost()]
        [Route("SignOn")]
        public async Task<InformedWorkerModels.Account> SignOn([FromBody]SignOnRequest model)
        {
            return await _accountRepository.SignOn(model.EmailAddress, model.Password);
        }
    }
}

I have also created a test project which references my DLL...

    [TestMethod]
    public async Task SignOn()
    {

        var webHostBuilder = new WebHostBuilder()
            .UseStartup<Startup>();

        using (var host = new TestServer(webHostBuilder))
        {
            using (var client = host.CreateClient())
            {
                var requestData = new SignOnRequest { EmailAddress = "emailAddress", Password= "password" };
                var content = new StringContent(JsonConvert.SerializeObject(requestData), Encoding.UTF8, "application/json");
                var response = await client.PostAsync("api/Registration/SignOn", content);
                //do some asserts here
            }
        }
    }

I get the error status code 404 not found.

What am i getting so wrong here please?


Solution

  • You have email and password as part of the route but send them as content in the body. that wont match the route template for the action and thus 404 Not Found.

    Create a model to hold the data

    public class SignOnRequest {
        [Required]
        public string emailAddress { get; set; }
        [Required]
        public string password { get; set; }
    }
    

    Also for core you have to specify with parameter attributes where the framework should look for parameters.

    [Route("api/[controller]")]
    public class RegistrationController : Controller{
    
        [HttpPost()]
        [Route("SignOn")] // Matches POST api/Registration/SignOn
        public async Task<IActionResult> SignOn([FromBody]SignOnRequest model) {
            if(ModelState.IsValid) {                
                var response = await _accountRepository.SignOn(model.emailAddress, model.password);
                return Ok(response);
            }
            return BadRequest();
        }
    }
    

    Which should now match the request being made in the integration test

    var requestData = new { emailAddress = "emailAddress", password = "password" };
    var content = new StringContent(JsonConvert.SerializeObject(requestData), Encoding.UTF8, "application/json");
    var response = await client.PostAsync("api/Registration/SignOn", content);
    

    As you also mentioned that the controller is in another assembly you would need to make sure that the controller is registered with the ServiceCollection within Startup so that the framework is aware of how to resolve the controller. Update the ConfigureServices to include

    services.AddMvc()
      .AddApplicationPart(typeof(RegistrationController).GetTypeInfo().Assembly)
      .AddControllersAsServices();
    

    as referenced from the following answer ASP.NET Core MVC controllers in separate assembly