How to Unit Test HTTP Handlers in C# - c#

I want to unit Test my HTTP Handlers. I am Using XUnit framework for unit testing, My Sample Handler Code is
public class MyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
var data = context.Request.InputStream;
//Here logic read the context.Request.InputStream
//All the data will be posted to this Stream
//Calling of business logic layer Methods
}
public bool IsReusable
{
get
{
return false;
}
}
}
I am new to Unit testing and I just want to know What are the possible test cases for testing my HandleS Since the return type of handlers is Void so I am confuse how can I write the Test for Handlers...
Note : I know HttpContextBase can called using an HttpContextWrapper, I am not asking this I am just asking What are the possible test cases for HttpHandlers

Testing void methods is an interesting topic. The main way I would go about it is to mock up an HTTP Context, and pass it in as a parameter. Then, you should be asserting on the things you are setting that HTTP context to do within your method. For example Assert.Equal("text/plain", myHttpContext.Response.ContentType); However, you need to ensure that you are creating a unit test. It says you have business logic after this step. Have you inverted your dependencies? (Passed them as parameters into the method). Are you able to mock things out appropriately? Make sure the method isn't doing an actual HTTP call (if it is a unit test). In general, I recommend less void methods with side affects but in legacy it can't be avoided. To address your initial question, test cases are going to be anything you logically expect. Did I set my route correctly? Am I expecting the content to be text? Is my payload accurate? Anything you change is a potential test, but ensure you are testing to acceptance criteria. Its okay to have multiple assertions in a test if its one logical assert.

Related

Is it best practice to test my Web API controllers directly or through an HTTP client?

I'm adding some unit tests for my ASP.NET Core Web API, and I'm wondering whether to unit test the controllers directly or through an HTTP client. Directly would look roughly like this:
[TestMethod]
public async Task GetGroups_Succeeds()
{
var controller = new GroupsController(
_groupsLoggerMock.Object,
_uowRunnerMock.Object,
_repoFactoryMock.Object
);
var groups = await controller.GetGroups();
Assert.IsNotNull(groups);
}
... whereas through an HTTP client would look roughly like this:
[TestMethod]
public void GetGroups_Succeeds()
{
HttpClient.Execute();
dynamic obj = JsonConvert.DeserializeObject<dynamic>(HttpClient.ResponseContent);
Assert.AreEqual(200, HttpClient.ResponseStatusCode);
Assert.AreEqual("OK", HttpClient.ResponseStatusMsg);
string groupid = obj[0].id;
string name = obj[0].name;
string usercount = obj[0].userCount;
string participantsjson = obj[0].participantsJson;
Assert.IsNotNull(name);
Assert.IsNotNull(usercount);
Assert.IsNotNull(participantsjson);
}
Searching online, it looks like both ways of testing an API seem to be used, but I'm wondering what the best practice is. The second method seems a bit better because it naively tests the actual JSON response from the Web API without knowing the actual response object type, but it's more difficult to inject mock repositories this way - the tests would have to connect to a separate local Web API server that itself was somehow configured to use mock objects... I think?
Edit: TL;DR
The conclusion you should do both because each test serves a different purpose.
Answer:
This is a good question, one I often ask myself.
First, you must look at the purpose of a unit test and the purpose of an integration test.
Unit Test :
Unit tests involve testing a part of an app in isolation from its
infrastructure and dependencies. When unit testing controller logic,
only the contents of a single action are tested, not the behaviour of
its dependencies or of the framework itself.
Things like filters, routing, and model binding will not work.
Integration Test :
Integration tests ensure that an app's components function correctly
at a level that includes the app's supporting infrastructures, such as
the database, file system, and network. ASP.NET Core supports
integration tests using a unit test framework with a test web host and
an in-memory test server.
Things like filters, routing, and model binding will work.
“Best practice” should be thought of as “Has value and makes sense”.
You should ask yourself Is there any value in writing the test, or am I just creating this test for the sake of writing a test?
Let's say your GetGroups() method looks like this.
[HttpGet]
[Authorize]
public async Task<ActionResult<Group>> GetGroups()
{
var groups = await _repository.ListAllAsync();
return Ok(groups);
}
There is no value in writing a unit test for it! because what you are doing is testing a mocked implementation of _repository! So what is the point of that?!
The method has no logic and the repository is only going to be exactly what you mocked it to be, nothing in the method suggests otherwise.
The Repository will have its own set of separate unit tests where you will cover the implementation of the repository methods.
Now let's say your GetGroups() method is more than just a wrapper for the _repository and has some logic in it.
[HttpGet]
[Authorize]
public async Task<ActionResult<Group>> GetGroups()
{
List<Group> groups;
if (HttpContext.User.IsInRole("Admin"))
groups = await _repository.FindByExpressionAsync(g => g.IsAdminGroup == true);
else
groups = await _repository.FindByExpressionAsync(g => g.IsAdminGroup == false);
//maybe some other logic that could determine a response with a different outcome...
return Ok(groups);
}
Now there is value in writing a unit test for the GetGroups() method because the outcome could change depending on the mocked HttpContext.User value.
Attributes like [Authorize] or [ServiceFilter(….)] will not be triggered in a unit test.
.
Writing integration tests is almost always worth it because you want to test what the process will do when it forms part of an actual application/system/process.
Ask yourself, is this being used by the application/system?
If yes, write an integration test because the outcome depends on a combination of circumstances and criteria.
Now even if your GetGroups() method is just a wrapper like in the first implementation, the _repository will point to an actual datastore, nothing is mocked!
So now, not only does the test cover the fact that the datastore has data (or not), it also relies on an actual connection being made, HttpContext being set up properly and whether serialisation of the information works as expected.
Things like filters, routing, and model binding will also work.
So if you had an attribute on your GetGroups() method, for example [Authorize] or [ServiceFilter(….)], it will be triggered as expected.
I use xUnit for testing so for a unit test on a controller I use this.
Controller Unit Test:
public class MyEntityControllerShould
{
private MyEntityController InitializeController(AppDbContext appDbContext)
{
var _controller = new MyEntityController (null, new MyEntityRepository(appDbContext));
var httpContext = new DefaultHttpContext();
var context = new ControllerContext(new ActionContext(httpContext, new RouteData(), new ActionDescriptor()));
_controller.ControllerContext = context;
return _controller;
}
[Fact]
public async Task Get_All_MyEntity_Records()
{
// Arrange
var _AppDbContext = AppDbContextMocker.GetAppDbContext(nameof(Get_All_MeetUp_Records));
var _controller = InitializeController(_AppDbContext);
//Act
var all = await _controller.GetAllValidEntities();
//Assert
Assert.True(all.Value.Count() > 0);
//clean up otherwise the other test will complain about key tracking.
await _AppDbContext.DisposeAsync();
}
}
The Context mocker used for unit testing.
public class AppDbContextMocker
{
/// <summary>
/// Get an In memory version of the app db context with some seeded data
/// </summary>
/// <param name="dbName"></param>
/// <returns></returns>
public static AppDbContext GetAppDbContext(string dbName)
{
//set up the options to use for this dbcontext
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(dbName)
.Options;
var dbContext = new AppDbContext(options);
dbContext.SeedAppDbContext();
return dbContext;
}
}
The Seed extension.
public static class AppDbContextExtensions
{
public static void SeedAppDbContext(this AppDbContext appDbContext)
{
var myEnt = new MyEntity()
{
Id = 1,
SomeValue = "ABCD",
}
appDbContext.MyENtities.Add(myEnt);
//add more seed records etc....
appDbContext.SaveChanges();
//detach everything
foreach (var entity in appDbContext.ChangeTracker.Entries())
{
entity.State = EntityState.Detached;
}
}
}
and for Integration Testing: (this is some code from a tutorial, but I can't remember where I saw it, either youtube or Pluralsight)
setup for the TestFixture
public class TestFixture<TStatup> : IDisposable
{
/// <summary>
/// Get the application project path where the startup assembly lives
/// </summary>
string GetProjectPath(string projectRelativePath, Assembly startupAssembly)
{
var projectName = startupAssembly.GetName().Name;
var applicationBaseBath = AppContext.BaseDirectory;
var directoryInfo = new DirectoryInfo(applicationBaseBath);
do
{
directoryInfo = directoryInfo.Parent;
var projectDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, projectRelativePath));
if (projectDirectoryInfo.Exists)
{
if (new FileInfo(Path.Combine(projectDirectoryInfo.FullName, projectName, $"{projectName}.csproj")).Exists)
return Path.Combine(projectDirectoryInfo.FullName, projectName);
}
} while (directoryInfo.Parent != null);
throw new Exception($"Project root could not be located using application root {applicationBaseBath}");
}
/// <summary>
/// The temporary test server that will be used to host the controllers
/// </summary>
private TestServer _server;
/// <summary>
/// The client used to send information to the service host server
/// </summary>
public HttpClient HttpClient { get; }
public TestFixture() : this(Path.Combine(""))
{ }
protected TestFixture(string relativeTargetProjectParentDirectory)
{
var startupAssembly = typeof(TStatup).GetTypeInfo().Assembly;
var contentRoot = GetProjectPath(relativeTargetProjectParentDirectory, startupAssembly);
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(contentRoot)
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json");
var webHostBuilder = new WebHostBuilder()
.UseContentRoot(contentRoot)
.ConfigureServices(InitializeServices)
.UseConfiguration(configurationBuilder.Build())
.UseEnvironment("Development")
.UseStartup(typeof(TStatup));
//create test instance of the server
_server = new TestServer(webHostBuilder);
//configure client
HttpClient = _server.CreateClient();
HttpClient.BaseAddress = new Uri("http://localhost:5005");
HttpClient.DefaultRequestHeaders.Accept.Clear();
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
/// <summary>
/// Initialize the services so that it matches the services used in the main API project
/// </summary>
protected virtual void InitializeServices(IServiceCollection services)
{
var startupAsembly = typeof(TStatup).GetTypeInfo().Assembly;
var manager = new ApplicationPartManager
{
ApplicationParts = {
new AssemblyPart(startupAsembly)
},
FeatureProviders = {
new ControllerFeatureProvider()
}
};
services.AddSingleton(manager);
}
/// <summary>
/// Dispose the Client and the Server
/// </summary>
public void Dispose()
{
HttpClient.Dispose();
_server.Dispose();
_ctx.Dispose();
}
AppDbContext _ctx = null;
public void SeedDataToContext()
{
if (_ctx == null)
{
_ctx = _server.Services.GetService<AppDbContext>();
if (_ctx != null)
_ctx.SeedAppDbContext();
}
}
}
and use it like this in the integration test.
public class MyEntityControllerShould : IClassFixture<TestFixture<MyEntityApp.Api.Startup>>
{
private HttpClient _HttpClient;
private const string _BaseRequestUri = "/api/myentities";
public MyEntityControllerShould(TestFixture<MyEntityApp.Api.Startup> fixture)
{
_HttpClient = fixture.HttpClient;
fixture.SeedDataToContext();
}
[Fact]
public async Task Get_GetAllValidEntities()
{
//arrange
var request = _BaseRequestUri;
//act
var response = await _HttpClient.GetAsync(request);
//assert
response.EnsureSuccessStatusCode(); //if exception is not thrown all is good
//convert the response content to expected result and test response
var result = await ContentHelper.ContentTo<IEnumerable<MyEntities>>(response.Content);
Assert.NotNull(result);
}
}
Added Edit:
In conclusion, you should do both, because each test serves a different purpose.
Looking at the other answers you will see that the consensus is to do both.
TL;DR
Is it best practice to test [...] directly or through an HTTP client?
Not "or" but "and". If you serious about best practices of testing - you need both tests.
First test is a unit test. But the second one is an integration test.
There is a common consensus (test pyramid) that you need more unit tests comparing to the number of integration tests. But you need both.
There are many reasons why you should prefer unit tests over integration tests, most of them boil down to the fact that unit test are small (in all senses) and integration tests - aren't. But the main 4 are:
Locality
When your unit test fails, usually, just from it's name you can figure out the place where the bug is. When integration test becomes red, you can't say right away where is the issue. Maybe it's in the controller.GetGroups or it's in the HttpClient, or there is some issue with the network.
Also, when you introduce a bug in your code it's quite possible that only one of unit tests will become red, while with integration tests there are more chances that more than one of them will fail.
Stability
With a small project which you can test on you local box you probably won't notice it. But on a big project with distributed infrastructure you will see blinking tests all the time. And that will become a problem. At some point you can find yourself not trusting test results anymore.
Speed
With a small project with a small number of tests you won't notice it. But on a bit project it will become a problem. (Network delays, IO delays, initialization, cleanup, etc., etc.)
Simplicity
You've noticed it yourself.
But that not always true. If you code is poorly structured, then it's easier to write integration tests. And that's one more reason why you should prefer unit tests. In some way they force you to write more modular code (and I'm not taking about Dependency Injection).
But also keep in mind that best practices are almost always about big projects. If your project is small, and will stay small, there are a big chance that you'll be better off with strictly opposite decisions.
Write more tests. (Again, that means - both). Become better at writing tests. Delete them latter.
Practice makes perfect.
If we limit the scope of discussion to Controller vs HttpClient testing comparison, I would say that it is better to use HttpClient. Because if you write tests for your controllers, you're already writing integration tests already and there is almost no point to write "weaker" integration tests while you can write stronger ones that is more realistic and also superset of the weaker ones.
For example, you can see from your own example that both of your test are testing exactly the same functionality. The different is that the latter one cover more area of testing -- JSON response, or can be something else like HTTP header you want to test. If you write the latter test, you don't need the first test at all.
I understand the pain of how to inject mocked dependencies. This requires more effort comparing to testing controller directly. However, .NET Core already provides a good set of tools to help you on that. You can setup the test host inside the test itself, configure it and get HttpClient from it. Then you can use that HttpClient for your testing purpose.
The other concern is that it is quite a tedious task to craft HttpClient's request for each test. Anyway, Refit can help you a lot on this. Refit's declarative syntax is quite easy to understand (and maintain eventually). While I would also recommend Refit for all remote API calls, it is also suitable for ASP.NET Core integration testing.
Combining all solutions available, I don't see why you should limit to controller test while you can go for more "real" integration test with only some little more effort.
I never have liked mocking in that as applications mature the effort spent on mocking can make for a ton of effort.
I like exercising endpoints by direct Http calls. Today there are fantastic tools like Cypress which allow the client requests to be intercepted and altered. The power of this feature along with easy Browser based GUI interaction blurs traditional test definitions because one test in Cypress can be all of these types Unit, Functional, Integration and E2E.
If an endpoint is bullet proof then error injection becomes impossible from outside. But even errors from within are easy to simulate. Run the same Cypress tests with Db down. Or inject intermittent network issue simulation from Cypress. This is mocking issues externally which is closer to a prod environment.
When doing unit test, it is important to know what are you going to test and write the tests based on your requirements. However, the second test, might looks like an integration test instead of an unit test, but I do not care to this point now!
Between your tests, I would recommend you to use the second option, because in the second unit test, you are testing your WebApi, as an WebApi, not as a class. For example suppose that you have a class with a method named X(). So how likely is it to write an unit test for it using Reflection? If it is completely unlikely, then writing an unit test based on Reflection is a waste of time. If it is likely, so you should write your test using Reflection too.
Moreover, using the second approach you are able to change the tech stack(For replace .Net with php) used for producing the WebApi, without changing you tests(This is what we expect from a WebApi too).
Finally, you should make a decision! how are you going to use this WebApi? How likely is it to call your WebApi using direct class instantiating?
Note:
It might be irrelevant to your question, but you should concentrate on your Asserts, too. For example asserting ResponseStatusCode and ResponseStatusMsg might not be needed and you can assert only one.
Or what will happen if obj is null? or obj has more than one member?
I'd say that they are not mutually exclusive. The first option is a classical unit test while the second is an integration test as involves more than a single unit of code.
If I had time to write either unit tests or integration tests, I'd pick unit tests as provides a more focused approach and gives, at least in my opinion, the best result from cost benefit.
In some particular projects where I had enough resources to write different suites of tests, I wrote both tests covering approaches. Where the second one would run without mocking anything (or maybe just the persistent storage) so I could test how all the components integrate together.
In relation to good practices, if you want to do real unit test, then you have no option but picking option one as no external dependencies are allowed (HttpClient is an external dependency).
Then, if the time and resources allow it, you could do integration testing for the most critical and/or complex paths.
If you are looking for some non programming You can use Postman, and can create collection of requests and can test multiple requests one by one.
You can use Swagger (aka OpenAPI).
Install Swashbuckle.AspNetCore from nuget.
using Microsoft.OpenApi.Models;
//in Startup.ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
}
//in Startup.Configure
public void Configure(IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
Finally, add "launchUrl": "swagger", in launchSettings.json

How write a unit test for asynchronous Web API

I have a project in C# and Framework 6. I have a WEB API to call the methods.
what is the best way to write a unit test for async WebAPI methods?
This is the method I have:
[HttpGet]
[Route("GetYears")]
public Task<IEnumerable<Year>> GetYearsAsync()
{
return reditApplicationsRepository.GetYearsAsync();
}
You can use await to do this too.
[TestMethod]
public async Task GetYearsTest()
{
//_sut is the System Under Test, in this case is an instance of your controller
var years = await _sut.GetYears();
//Any Assert that you want
}
And to stub the return of the method creditApplicationsRepository.GetYearsAsync(); use this Task.FromResult(mockReturn).
The key objective of a unit test is to test the functionality of the code inside a method. So async or not should not be particularly relevant functionally.
So I would say do something like this:
[TestMethod]
public void GetYearsTest(){
//This would assume that your web api class has a mockable repository
var yourClass = new YourClass(new MockRepository());
//this is going to run your codes synchronously so you can get an immediate result to test on.
var years = yourClass.GetYears().Result;
//Run whatever test you want to on this
Assert.IsNotNull(years);
}
If the purpose of running an Async test is to determine if your code is thread safe you will have to get creative to determine how to properly test your code to ensure it is in fact safe. In order for me to assist with that I would need to know more about the repo that is being used here or what variables could potentially get thread locked or result in an error.

Unit testing Web Service responses

I am currently writing an API wrapper in C# for ResellerClub's REST/HTTP API, which provides responses in garden-variety JSON objects. Invocation is performed by performing HTTP POST/GET on API endpoints using the HttpClient class. JSON.Net is used for parsing the responses.
How I can unit test my API wrapper functionality for the API as most calls require a level of expected state in order to succeed. For example, I cannot test the creation of a CNAME record on a domain that I have not already registered.
I understand that tests should never rely on state which they do not arrange themselves, and I've also been told that the tests should never actually deal with any kind of persistence mechanism such as a database. So, for the above example of a CNAME record, that as part of the "Arrange" phase of the test I should register a test domain, assert it worked, then do the actual CNAME function?
Alternative, should I come up with some way of mocking the JSON responses that are returned from the Reseller Club API?
EDIT: Example of my API class (ResellerClubApi.cs)
private async Task<string> DownloadString(string uri)
{
// HttpClient object downloads the JSON response string asynchronously
}
The DownloadString() method is used by my functionality as a generic means of grabbing the response from the third party service.
public async Task<List<string>> SuggestNames(string domainName)
{
// Calls DownloadString() with the correct URI, uses Newtonsoft.JSON to parse
// string representation of JSON into object
}
Methods such as SuggestNames() above are called like this from the higher service layer
public void someServiceLayerMethod()
{
var rcApi = new ResellerClubApi();
var x = rcApi.SuggestNames("something");
// ...
}
As you can see, I am a bit stuck as to how to mock JSON responses from the likes of HttpClient when my ResellerClubApi class is the lowest possible layer of my own code prior to doing things over HTTP.
I also don't know how to start using IoC to hand the HttpClient dependency...
Thanks
I would separate the code from your ResellerClubApi class which involves downloading stuff and authorization, and everything that involves connecting to a remote service, in let's say a ResellerClubClient and have it implement a IResellerClubClient interface.
public interface IResellerClubClient {
string RequestJson(string url);
}
public class ResellerClubClient : IResellerClubClient {
// implement your methods here
}
public ResellerClubApi : IResellerClubApi {
private readonly IResellerClubClient client;
// Pass the client as dependency, either manually or using Dependency framework of your choice
public ResellerClubApi(IResellerClubClient client) {
this.client = client;
}
public List<string> SuggestNames(string domainName) {
var jsonString = this.client.RequestJson("http://example.com/domains/?name="+domainName);
// decode it and do something with it
}
}
This allows you to test your ResellerClubApi class without being depending on a concrete IResellerClubClient implementation. And the best is, you can change it (from HttpClient to socket or whatever and don't ever have to touch your ResellerClubApi.
And then set up your Unit test in framework of your choice. Some example with Moq framework:
var mockedJsonString = '{ succes: true, names: ["domainA.com", "domainA.us"] }';
// create mockup object using IResellerClubClient interface
var resellerClubClient = new Mock<IResellerClubClient>();
// Tell the mock object to return "mockedJsonString" when any parameter is passed to RequestJsonString.
// If you do more than 1 call in a test, or if that's expected to be called multiple times inside
// the method to be tested, you can setup multiple conditions and results this way too
resellerClubClient.Setup(x => x.RequestJson(It.IsAny<string>())).Returns(mockedJsonString);
var api = new ResellerClubApi(resellerClubClient.Object);
List<string> names = api.SuggestNames("domain.com");
// do your assertions here
By having abstracted the connection and data retrieving methods into hit's own class represented by an interface, you made your Api class UnitTestable and easy to mock server responses.
Of course, the ResellerClubClient can't be Unit tested of course. But it can be done in an integration test or a verification test. A UnitTest should never involve connecting to a server or a database.
Here is a way to do it by mocking the HttpMessageHandler using Moq unit test. http://geekswithblogs.net/abhi/archive/2013/11/20/unit-tests-for-httpclient-using-httpmessagehandler.aspx

How to write Functional Tests against ServiceStack API

We have an ASP.NET Web Application wired up with ServiceStack. I've never written functional tests before, but have been tasked to write tests (nUnit) against our API and prove it's working all the way down to the Database level.
Can someone help me get started writing these tests?
Here's an example of a post method on our Users service.
public object Post( UserRequest request )
{
var response = new UserResponse { User = _userService.Save( request ) };
return new HttpResult( response )
{
StatusCode = HttpStatusCode.Created,
Headers = { { HttpHeaders.Location, base.Request.AbsoluteUri.CombineWith( response.User.Id.ToString () ) } }
};
}
Now I know how to write a standard Unit Test, but am confused on this part. Do I have to call the WebAPI via HTTP and initialize a Post? Do I just call the method like I would a unit test? I suppose it's the "Functional Test" part that eludes me.
Testing the service contract
For an end-to-end functional test, I focus on verifying that the service can accept a request message and produce the expected response message for simple use cases.
A web service is a contract: given a message of a certain form, the service will produce a response message of a given form. And secondarialy, the service will alter the state of its underlying system in a certain way. Note that to the end client, the message is not your DTO class, but a specific example of a request in a given text format (JSON, XML, etc.), sent with a specific verb to a specific URL, with a given set of headers.
There are multiple layers to a ServiceStack web service:
client -> message -> web server -> ServiceStack host -> service class -> business logic
Simple unit testing and integration testing is best for the business logic layer. It's usually easy write unit tests directly against your service classes too: it should be easy to construct a DTO object, call a Get/Post method on your service class, and validate the response object. But these do not test anything that's happening inside the ServiceStack host: routing, serialization/deserialization, execution of request filters, etc. Of course, you don't want to test the ServiceStack code itself as that's framework code that has its own unit tests. But there is an opportunity to test the specific path that a specific request message takes going into the service and coming out of it. This is the part of the service contract that can't be fully verified by looking directly at the service class.
Don't try for 100% coverage
I would not recommend trying to get 100% coverage of all business logic with these functional tests. I focus on covering the major use cases with these tests - one or two reqest examples per endpoint usually. Detailed testing of specific business logic cases is much more efficiently done by writing traditional unit tests against your business logic classes. (Your business logic and data access are not implemented in your ServiceStack service classes, right?)
The implementation
We are going to run a ServiceStack service in-process and use an HTTP client to send requests to it and then verify the content of the responses. This implementation is specific to NUnit; a similar implementation should be possible in other frameworks.
First, you need an NUnit setup fixture that runs one before all of your tests, to set up the in-process ServiceStack host:
// this needs to be in the root namespace of your functional tests
public class ServiceStackTestHostContext
{
[TestFixtureSetUp] // this method will run once before all other unit tests
public void OnTestFixtureSetUp()
{
AppHost = new ServiceTestAppHost();
AppHost.Init();
AppHost.Start(ServiceTestAppHost.BaseUrl);
// do any other setup. I have some code here to initialize a database context, etc.
}
[TestFixtureTearDown] // runs once after all other unit tests
public void OnTestFixtureTearDown()
{
AppHost.Dispose();
}
}
Your actual ServiceStack implementation probably has an AppHost class that's a subclass of AppHostBase (at least if it's running in IIS). We need to subclass a different base class to run this ServiceStack host in-process:
// the main detail is that this uses a different base class
public class ServiceTestAppHost : AppHostHttpListenerBase
{
public const string BaseUrl = "http://localhost:8082/";
public override void Configure(Container container)
{
// Add some request/response filters to set up the correct database
// connection for the integration test database (may not be necessary
// depending on your implementation)
RequestFilters.Add((httpRequest, httpResponse, requestDto) =>
{
var dbContext = MakeSomeDatabaseContext();
httpRequest.Items["DatabaseIntegrationTestContext"] = dbContext;
});
ResponseFilters.Add((httpRequest, httpResponse, responseDto) =>
{
var dbContext = httpRequest.Items["DatabaseIntegrationTestContext"] as DbContext;
if (dbContext != null) {
dbContext.Dispose();
httpRequest.Items.Remove("DatabaseIntegrationTestContext");
}
});
// now include any configuration you want to share between this
// and your regular AppHost, e.g. IoC setup, EndpointHostConfig,
// JsConfig setup, adding Plugins, etc.
SharedAppHost.Configure(container);
}
}
Now you should have an in-process ServiceStack service running for all of your tests. Sending requests to this service is pretty easy now:
[Test]
public void MyTest()
{
// first do any necessary database setup. Or you could have a
// test be a whole end-to-end use case where you do Post/Put
// requests to create a resource, Get requests to query the
// resource, and Delete request to delete it.
// I use RestSharp as a way to test the request/response
// a little more independently from the ServiceStack framework.
// Alternatively you could a ServiceStack client like JsonServiceClient.
var client = new RestClient(ServiceTestAppHost.BaseUrl);
client.Authenticator = new HttpBasicAuthenticator(NUnitTestLoginName, NUnitTestLoginPassword);
var request = new RestRequest...
var response = client.Execute<ResponseClass>(request);
// do assertions on the response object now
}
Note that you may have to run Visual Studio in admin mode in order to get the service to successfully open that port; see comments below and this follow-up question.
Going further: schema validation
I work on an API for an enterprise system, where clients pay a lot of money for custom solutions and expect a highly robust service. Thus we use schema validation to be absolutely sure we don't break the service contract at the lowest level. I don't think schema validation is necessary for most projects, but here's what you can do if you want to take your testing a step further.
One of the ways in which you can inadventently break your service's contract is to change a DTO in a way that is not backward compatible: e.g., rename an existing property or alter custom serialization code. This can break a client of your service by making data no longer available or parseable, but you typically can't detect this change by unit testing your business logic. The best way to prevent this from happening is to keep your request DTOs separate and single-purpose and separate from your business/data access layer, but there's still a chance someone will accidentally apply a refactoring incorrectly.
To guard against this, you can add schema validation to your functional test. We do this only for specific use cases that we know a paying client is actually going to use in production. The idea is that if this test breaks, then we know that the code that broke the test would break this client's integration if it were to be deployed to production.
[Test(Description = "Ticket # where you implemented the use case the client is paying for")]
public void MySchemaValidationTest()
{
// Send a raw request with a hard-coded URL and request body.
// Use a non-ServiceStack client for this.
var request = new RestRequest("/service/endpoint/url", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(requestBodyObject);
var response = Client.Execute(request);
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
RestSchemaValidator.ValidateResponse("ExpectedResponse.json", response.Content);
}
To validate the response, create a JSON Schema file that describes the expected format of the response: what fields are are required to exist for this specific use case, what data types are expected, etc. This implementation uses the Json.NET schema parser.
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
public static class RestSchemaValidator
{
static readonly string ResourceLocation = typeof(RestSchemaValidator).Namespace;
public static void ValidateResponse(string resourceFileName, string restResponseContent)
{
var resourceFullName = "{0}.{1}".FormatUsing(ResourceLocation, resourceFileName);
JsonSchema schema;
// the json file name that is given to this method is stored as a
// resource file inside the test project (BuildAction = Embedded Resource)
using(var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFullName))
using(var reader = new StreamReader(stream))
using (Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFileName))
{
var schematext = reader.ReadToEnd();
schema = JsonSchema.Parse(schematext);
}
var parsedResponse = JObject.Parse(restResponseContent);
Assert.DoesNotThrow(() => parsedResponse.Validate(schema));
}
}
Here's an example of a json schema file. Note that this is specific to this one use case and is not a generic description of the response DTO class. The properties are all marked as required as these are the specific ones the client are expecting in this use case. The schema might leave out other unused properties that currently exist in the response DTO. Based on this schema, the call to RestSchemaValidator.ValidateResponse will fail if any of the expected fields are missing in the response JSON, have unexpected data types, etc.
{
"description": "Description of the use case",
"type": "object",
"additionalProperties": false,
"properties":
{
"SomeIntegerField": {"type": "integer", "required": true},
"SomeArrayField": {
"type": "array",
"required": true,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"Property1": {"type": "integer", "required": true},
"Property2": {"type": "string", "required": true}
}
}
}
}
}
This type of test should be written once and never modified unless the use case it's modeled on becomes obsolete. The idea is that these tests will represent actual usages of your API in production and ensure that the exact messages your API promises to return do not change in a way that breaks existing usages.
Other info
ServiceStack itself has some examples of running tests against an in-process host, on which the above implementation is based.

C# Unit Testing: Testing a method that uses MapPath

First of all, I am aware that this question is dangerously close to:
How to MapPath in a unit test in C#
I'm hoping however, that it has a different solution. My issue follows:
In my code I have an object that needs to be validated. I am creating unit tests for each validation method to make sure it is validating correctly. I am creating mock data and loading it into the object, then validating it. The problem is that within the validation, when an error occurs, an error code is assigned. This error code is used to gather information about the error from an xml file using Server.MapPath. However, when trying to get the xml file, an exception is thrown meaning the file cannot be found.
Since MapPath is in my validation code, and not my unit test, how do I get my unit test to recognize the path? Does this question make sense?
Error Line (In my Validation code NOT my unit test):
XDocument xdoc = XDocument.Load(HttpContext.Current.Server.MapPath("App_Data/ErrorCodes.xml"));
Simplified: The Unit Test calls a method in my program that calls Server.MapPath which then fails.
I would abstract out the "filename provider" into an class that simply returns a location, then you can mock it much, much easier.
public class PathProvider
{
public virtual string GetPath()
{
return HttpContext.Current.Server.MapPath("App_Data/ErrorCodes.xml");
}
}
Then, you can either use the PathProvider class directly...
PathProvider pathProvider = new PathProvider();
XDocument xdoc = XDocument.Load(pathProvider.GetPath());
Or mock it out in your tests:
PathProvider pathProvider = new MockPathProvider(); // using a mocking framework
XDocument xdoc = XDocument.Load(pathProvider.GetPath());
After some rigorous googling and some help from a colleague we came up with a simple solution already built into .net
Above the unit tests that accesses the validation process, I added:
[TestMethod()]
[HostType("ASP.NET")]
[UrlToTest("http://localhost:###/upload_file.aspx")]
[AspNetDevelopmentServerHost("Path To Web Application", "Path To Web Root")]
This works perfectly. Basically, when the test is called, it loads the URL with the specified unit test in the page load. Since it is a web site that is now calling the unit test, the validation will have access to Server.MapPath. This solution may not work for everyone, but it was perfect for this. Thanks to all you contributed.
Try using Rhino Mocks or an alternative mocking framework to Mock the httpContext (or other dependent objects)
Or you could write your own mock objects.
Or write a MapPathWrapper class, inherit from a MapPathWrapperBase class for your real environment, then in for your unit tests create a MockMapPathWrapper object.
There should be plenty of examples for mocking on SO.
Here's one I asked:
How to use Rhino Mocks to Mock an HttpContext.Application
UPDATE
I only have experience doing this with the Asp.Net MVC, with webforms I imagein it would be a lot more difficult because of the lack of an HttpContextBase class.
I would extract methods that accept your dependencies as arguments:
public void Validate(HttpContext context)
{
ValidatePath(context.Server.MapPath("App_Data/ErrorCodes.xml"));
}
public void ValidatePath(string path)
{
XDocument xdoc = XDocument.Load(path);
ValidateDocument(xdoc);
}
public void ValidateDocument(XDocument xdoc)
{
// Original code
}
You can then test the various methods independently. For example, testing how ValidatePath() handles a missing file.

Categories