Integration testing is a technique employed to assert whether an end-to-end scenario is working - where all pieces of the software components (typically non-user-interaction interface) are being tested together. For the article today, I am going to talk about how we can achieve integration testing with the Web API we have created - that is secured by Azure AD OAuth.
Let's start off being evaluating some sample Web API code we have written.
[RoutePrefix("account")] public class AccountController : ApiController { ... [HttpPut, Route] public AccountDto Add(AccountDto accountDto) { ... } ... }
When we think about integration testing on the Web API level, we are really interested in the response returned when an API is being invoked. For clarity, I have removed the code within the Add Account API for now, so we can concentrate on the signature.
So, the first question is, should we simply instantiate the AccountController and make some calls to it? Well, we can, but it would not be considered sufficient since a true HTTP request should return some true HTTP response where StatusCode would be return to indicate the success or failure and not some .NET exception (which should really have been tested in some lower level unit testing anyway).
We don't really want to spend the time and energy trying to host this within an actual IIS. This would not be easy to debug as well as maintain. Fortunately, with OWIN, we are able to host our own web server. Let's take a look at my Startup code. Most of the code seen here is pretty much standard when you start a new Web API project so there's not much to say about it. My Global.ascx is pretty much empty as seen below because all my code is now running via the OWIN plumbing. This would be one of the refactoring you would have to do if you are going to do this.
[assembly: OwinStartup(typeof(SampleProject.Web.Api.Startup))] namespace SampleProject.Web.Api { public partial class Startup { public void Configuration(IAppBuilder app) { var configuration = new HttpConfiguration(); ... WebApiConfig.Register(configuration); ConfigureAuth(app); app.UseWebApi(configuration); }
public class SampleProjectWebApiApplication : System.Web.HttpApplication { protected void Application_Start() { } }
Let's take a look at ConfigureAuth which is important part of how we are securing Web API with Azure AD OAuth. For more detailed information on the implementation below and how to configure your application, users, assignments etc in Azure, please check out my blog on Securing our Web API application with Azure AD OAuth first.
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { app.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Tenant = ConfigurationManager.AppSettings["ida:Tenant"], TokenValidationParameters = new TokenValidationParameters { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] }, }); } }
If you are familiar with Azure AD OAuth, you might be thinking about how we plan to do the OAuth dance because we have a need to redirect users to Microsoft and back for authentication to get the token! Fortunately, I found a post by Vittorio Bertocci who showed me how. We can pass in a username and password and get a token back which will avoid user interaction. With power, comes responsibility. Let's pay close attention to his warning - in our context, this is only for testing in lower environment! Do not ever use this technique in Production code.
To use this technique, let's install the Nuget package: Microsoft.IdentityModel.Clients.ActiveDirectory.
The following code is a code snippet of how we can get the OAuth token back. Notice we can pass in username and password. The action is an Action parameter I am passing so I can simply start using the HttpClient on a higher level without having to write this code each time.
... var context = new AuthenticationContext("https://login.windows.net/smartpropertymanagement.onmicrosoft.com", true); var authenticationResult = await context.AcquireTokenAsync("MY API", "MY CLIENT ID", new UserCredential("USERNAME", "PASSWORD")); using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken); ... return action(httpClient); }
We can possibly also cache the OAuth token. It would have to be refreshed if it expires. Refer to the ExpiresOn property. The RefreshToken property can be passed in to get a new token. You can decide on how you want to implement caching.
We got to start our web server when our unit tests first runs, and shutdown when it all ends. Since I am using NUnit, there is a way to do this. The caveat is that all tests have to be in the same namespace. Let's see what it looks like.
////// Important! This has to be in the same namespace as where all the /// controllers are, so as to be invoked! /// [SetUpFixture] public class AssemblySetup { private static IDisposable _webApp; [SetUp] public void Setup() { var startOptions = new StartOptions("http://URL:PORT"); _webApp = WebApp.Start(startOptions); } [TearDown] public void Teardown() { Thread.Sleep(500); _webApp.Dispose(); } }]
So now, we can write a unit test with our http client instance created from using the technique of passing in a username and password to get a token and injecting it as a bearer header each http request .
... [Test)] public void CanCreateNewAccount() { ... using (var result = httpClient.Put(model, "account").Result) { var content = result.Content.ReadAsStringAsync().Result; result.EnsureSuccessStatusCode(); Assert.That(result, Is... }
With OWIN, we have the ability to host our own web server easily without writing much code. With Azure AD, we don't have to worry about writing some custom hooks to authenticate each request. Our testings can now best represent what happens on an actual environment. I hope this article helps you in your path to a more successful testing experience and better software quality!
Hello David,
ReplyDeleteThe Article on Integration testing our Web API with Azure AD OAuth is informative. It gives detailed information about it .Thanks for Sharing the information on API Integration testing. Software Testing Company