Integration testing an MVC application without the

2019-03-30 06:44发布

问题:

I'm starting development on a new MVC application that will be largely written using TDD. I'd like to add some integration tests to ensure that the fully wired application (I'm using StructureMap for IOC, NHibernate for persistence) works as expected.

While I intend to write a few functional smoke tests with Selenium, for maintainability reasons, I'd rather do my most of integration testing by directly calling actions on my controllers using good old C#.

There is surprisingly little guidance on how one would accomplish this, so I took a stab on a plan of attack

  1. Pull all bootstrapping code out of Global.asax and into a separate class
  2. Attempt to leverage MvcContrib-TestHelper or similar to create ASP.NET dependencies (Context, Request, etc)

I've accomplished step 1, but really have no idea how to proceed to step 2. Any guidance would be appreciated.

public class Bootstrapper
{              
    public static void Bootstrap()
    {
        DependencyResolverInitializer.Initialize();
        FilterConfig.RegisterFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        ModelBinders.Binders.DefaultBinder = new SharpModelBinder();
    }           
}

public class DependencyResolverInitializer
{
    public static Container Initialize()
    {
        var container = new Container();
        container.Configure(x => x.Scan(y =>
        {
            y.Assembly(typeof(Webmin.UI.FilterConfig).Assembly);
            y.WithDefaultConventions();
            y.LookForRegistries();

        }));

        DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
        return container;
    }
}

public class StructureMapDependencyResolver : IDependencyResolver
{
    private readonly IContainer _container;

    public StructureMapDependencyResolver(IContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType.IsAbstract || serviceType.IsInterface) {
            return _container.TryGetInstance(serviceType);
        }
        return _container.GetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType).Cast<object>();
    }
}

回答1:

If you want to do automated end-to-end testing of an ASP.NET MVC application without going through the UI, one good way to do it is to programmatically send HTTP requests to the different URLs and assert the state of the system afterwards.

Your integration tests would basically look like this:

  1. Arrange: Start a web server to host the web application under test
  2. Act: Send an HTTP request to a specific URL, which will be handled by a controller action
  3. Assert: Verify the state of the system (e.g. look for specific database records), or verify the contents of the response (e.g. look for specific strings in the returned HTML)

You can easily host an ASP.NET web app in an in-process web server using CassiniDev. Also, one convenient way to send HTTP requests programmatically is to use the Microsoft ASP.NET Web API Client Libraries.

Here's an example:

[TestFixture]
public class When_retrieving_a_customer
{
    private CassiniDevServer server;
    private HttpClient client;

    [SetUp]        
    public void Init()
    {
        // Arrange
        server = new CassiniDevServer();
        server.StartServer("..\relative\path\to\webapp", 80, "/", "localhost");
        client = new HttpClient { BaseAddress = "http://localhost" };
    }

    [TearDown]
    public void Cleanup()
    {
        server.StopServer();
        server.Dispose();
    }

    [Test]
    public void Should_return_a_view_containing_the_specified_customer_id()
    {
        // Act
        var response = client.GetAsync("customers/123").Result;

        // Assert
        Assert.Contains("123", response.Content.ReadAsStringAsync().Result);
    }
}

If you're looking for a more complete example of this technique in action, you can find it in a sample MVC 4 web application of mine, where I demonstrated it in the context of writing automated acceptance tests.