Crowbar is an application testing library for ASP.NET MVC 3 and 4.
Crowbar was inspired by the Nancy.Testing project from Nancy by Andreas HĂĄkansson, Steven Robbins and contributors. The Crowbar API is highly influenced by the Nancy.Testing API.
The source code of Crowbar is based on Steven Sanderson's MvcIntegrationTestFramework. The initial commit of Crowbar is Jon Canning's fork of the MvcIntegrationTestFramework.
Crowbar employs James Treworgy's jQuery port CsQuery for CSS selection and DOM manipulation. CsQuery is the only third-party dependency of Crowbar.
The MvcApplication
class (both generic and non-generic) is the heart of Crowbar and represents a proxy to the ASP.NET MVC project. Please note that creating the MvcApplication
instance is a time-consuming process and should preferably only be done once, e.g., in a test base class.
An instance of MvcApplication
can be created in two ways.
public class MvcApplicationTests
{
private static readonly MvcApplication Application =
MvcApplication.Create("<name of your ASP.NET MVC project>");
[Test]
public void Should_return_html()
{
Application.Execute(browser =>
{
var response = browser.Get("/route");
response.ShouldBeHtml();
});
}
}
public class MvcApplicationTests
{
protected static readonly MvcApplication<UserDefinedContext> Application =
MvcApplication.Create<UserDefinedProxy, UserDefinedContext>("<name of your ASP.NET MVC project>");
[Test]
public void Should_return_html()
{
Application.Execute((browser, context) =>
{
// Use any state in the context to bootstrap your test.
var response = browser.Get("/route");
response.ShouldBeHtml();
});
}
}
In order to be able to pass state from the server (the MVC application) to the test case it is possible to define a user-defined context. The context must implement IDisposable
. The context will be created for each test and will be disposed after the test has been run.
public class UserDefinedContext : IDisposable
{
// Define any state from the server.
public void Dispose()
{
// Will be disposed after the test case has been run.
}
}
In order to initialize the user-defined context we need to defined our own proxy. This is done by deriving from MvcApplicationProxyBase<TContext>
.
public class UserDefinedProxy : MvcApplicationProxyBase<UserDefinedContext>
{
protected override UserDefinedContext CreateContext(HttpApplication application, string testBaseDirectory)
{
// You probably want to cast the 'application' to your derived HttpApplication class.
return new UserDefinedContext
{
// Set any state.
};
}
}
By default Crowbar tries to use the Web.config defined in the ASP.NET MVC project, even though this is somewhat instable at times. To counter any problems with the configuration of the MVC project it is possible and highly recommended to use a custom configuration file. The name of the custom configuration file can be supplied as the second argument to MvcApplication.Create()
. The custom configration file should be defined in the test project. Please note that the configuration file must be copied to the output directory by setting Copy to Output Directory to either Copy always or Copy if newer.
private static readonly MvcApplication Application =
MvcApplication.Create("<name of your ASP.NET MVC project>", "Web.Custom.config");
It is possible to define default BrowserContext
settings for each request by supplying a third argument to MvcApplication.Create()
. These settings will be applied to the BrowserContext
before the request-specific settings. See the section on BrowserContext for available options.
private static readonly MvcApplication Application =
MvcApplication.Create("<name of your ASP.NET MVC project>", "Web.Custom.config", ctx => ctx.HttpsRequest());
An instance of the Browser
class enables us to interact with ASP.NET MVC application by performing requests.
var response = browser.PerformRequest("GET", "/route");
The PerformRequest
method has an optional third argument which let us customize the request.
var response = browser.PerformRequest("GET", "/route", ctx => {
// Customize the request (BrowserContext object).
});
For convenience, the most common HTTP methods have their own methods: Delete
, Get
, Post
, Put
, which delegate to PerformRequest
. PerformRequest
returns an instance of BrowserResponse
.
The Submit
and AjaxSubmit
extensions try to mimic the browser form submission behavior. This is done by extracting the form action, the form method, any form values for the supplied HTML and performing the request based on these values.
var response = browser.Submit("<form action='/route' method='post'>...</form>", model);
Loads HTML from the server using GET and forwards it to either Submit
or AjaxSubmit
.
var continuation = browser.Load("/path/to/form", ctx => { // Modify the GET request. });
var response = continuation.Submit(model);
Renders HTML by reading it from disk and forwards it to either Submit
or AjaxSubmit
. The first argument can be either a string (the name of the view) or a PartialViewContext
object. Using the partial view context it is possible to state whether client validation and/or unobtrusive JavaScript is enabled and to set the principal that should be used when rendering the view which is important when the form is rendered with an anti-forgery request token.
var continuation = browser.Render("~/path/to/form.cshtml", model);
var response = continuation.Submit();
var context = new PartialViewContext("~/path/to/form.cshtml")
{
ClientValidationEnabled = true,
UnobtrusiveJavaScriptEnabled = true
};
context.SetFormsAuthPrincipal("username");
var response = browser.Render(context, model).Submit();
The BrowserContext
class defines the context of the HTTP request. As previously stated Browser.PerformRequest()
provides us with the opportunity to customize the HTTP request.
To mark the request as an AJAX request call AjaxRequest()
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.AjaxRequest();
});
To set the body of the request use Body(string body, string contentType
). The second parameter is optional (the default content type is application/octet-stream).
To supply a cookie with the request use Cookie(HttpRequest cookie)
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
HttpCookie = ...
ctx.Cookie(cookie);
});
To provide a form value with the request use FormValue(string key, string value)
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.FromValue("key", "value");
});
To provide an HTTP header with the request use Header(string name, string value)
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.Header("name", "value");
});
To mark the request as using the HTTP protocol (the default value) use HttpRequest()
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.HttpRequest();
});
To mark the request as using the HTTPS protocol use HttpsRequest()
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.HttpsRequest();
});
To provide a query string entry with the request use Query(string key, string value)
.
var response = browser.PerformRequest("<method>", "<route>", ctx => {
ctx.Query("key", "value");
});
Crowbar provides several extension method to BrowserContext
.
Supplies a forms authentication cookie with the request.
Sets the content type to application/json (overridable) and provides a JSON object as the body of the request.
Sets the content type to application/xml (overridable) and provides an XML object as the body of the request.
The BrowserResponse
class defines the properties of the HTTP response.
The Advanced
property provides access to various ASP.NET MVC contexts which are collected during the request.
Returns the content type of the request.
Provides access to the Location header if the response is a redirect.
Returns the string representation of the body of the response.
Returns the HTTP status code of the response.
Returns a CsQuery object (similar to a jQuery object) which provides CSS selection and DOM manipulation functionality.
Returns a JSON object as a dynamic
object.
Returns an XML object as an XElement
object.
Crowbar provides several extension methods for easing the assertion of the correct behavior of the request.
Asserts that the HTTP status code is 'HTTP Status 200 OK' and that the content type is text/html. It provides an optional argument for performing additional assertions on the returned HTML document (CsQuery
). The CsQuery
object is also returned from the method.
response.ShouldBeHtml(document => {
var main = document["#main"];
Assert.That(main, Has.Length.EqualTo(1));
});
Asserts that the HTTP status code is 'HTTP Status 200 OK' and that the content type is application/json (overridable). It provides an optional argument for performing additional assertions on the returned JSON object (dynamic
). The dynamic
object is also returned from the method.
response.ShouldBeJson(json => {
Assert.That(json.value, Is.EqualTo("Crowbar"));
});
Asserts that the HTTP status code is 'HTTP Status 200 OK' and that the content type is application/xml (overridable). It provides an optional method for performing additional assertions on the returned XML object (XElement
). The XElement
object is also returned from the method.
response.ShouldBeXml(xml => {
Assert.That(xml.Value, Is.EqualTo("Crowbar"));
});
Asserts that the response has or has not a cookie with the specified name (and value).
response.ShouldHaveCookie("<name of cookie>");
response.ShouldHaveCookie("<name of cookie>", "<value of cookie");
response.ShouldNotHaveCookie("<name of cookie>");
Asserts that the HTTP status code is 'HTTP Status 301 MovedPermanently', that the header Location is defined and that the value of the header is equal to the expected location.
response.ShouldHavePermanentlyRedirectTo("/location");
Asserts that the HTTP status code is 'HTTP Status 302 Found', that the header Location is defined and that the value of the header is equal to the expected location.
response.ShouldHaveTemporarilyRedirectTo("/location");
The project contains two sample projects: Raven.Web/Raven.Tests and Tool.Web/Tool.Tests. The Raven sample project shows how to implement a custom user-defined proxy and context.
Crowbar is built using the ASP.NET MVC 3 assembly. If you're using ASP.NET MVC 4 and experience odd behavior consider adding a binding redirect in the root Web.config.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<configuration>
v0.9.3
- Added the option of specifying a default
BrowserContext
(when creating theMvcApplication
) that will be applied to every request. - Additional assert helper,
ShouldNotHaveCookie
, for HTTP cookies.
v0.9.2
- Added
BrowserResponse.RawHttpRequest
which returns a raw string representation of the HTTP request (similar to Fiddler). If the server throws an exception the raw HTTP request will be included in the exception message for easier troubleshooting. - Assert helpers,
ShouldHaveCookie
, for HTTP cookies. - Added
Html
toBrowserLoadContinuation
for easier troubleshooting. - Added
Cookies
andHtml
toBrowserRenderContinuation<TContext>
for easier troubleshooting. - More descriptive error messages for
Browser.Submit()
.
v0.9.1
CrowbarController
,As
(new) andDelegateExtensions
(new) are now part of the public API.- Upgraded CsQuery to 1.3.4.
- Changes to the build script, enabled package restore.
v0.9
- Initial public release.