---
title: Testing MVC controllers
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

You can test the logic of your MVC controllers that work with content items, such as articles or products, info objects as well as any other objects. This is done by providing fake representations of these objects. Using this approach, you can create the necessary objects without accessing the database.

The recommended approach for providing fake objects is to use repositories (adapters) that, for content items, make use of [generated](https://docs.kentico.com/13/developing-websites/generating-classes-for-xperience-objects.md) classes. Repositories provide the following advantages over using [DocumentQuery calls](https://docs.kentico.com/13/custom-development/working-with-pages-in-the-api.md) directly in controllers:

- Your tests will work regardless of the used methods. This is important as not all document query methods are supported when creating fake objects.
- Repositories only need to contain the methods the application really needs. This allows you to make changes to the implementation in a much smaller scale than if you were using document query.

We also recommend writing individual controllers to use dependency injection and retrieve their dependencies via their constructors. This approach allows you to create classes with clearly defined responsibilities, which are, in turn easier to test.

You can also use containers such as [Autofac](http://autofac.org/) (or similar container implementations) to simplify the process of creating controllers and their dependencies, as well as handling the life cycle of the dependencies.

See also: [Initializing Xperience services with dependency injection](https://docs.kentico.com/13/developing-websites/initializing-xperience-services-with-dependency-injection.md)

> **Tip:** See the project code of the Dancing Goat MVC sample site for a reference on how to implement tests for your controllers.

## Providing content items in controller tests

The following example demonstrates the implementation of a sample _ArticleController_ controller and corresponding _ArticleControllerTests_. The controller displays article details. Note that the sample implementation is dependent on external libraries, such as [Autofac](http://autofac.org/), [TestStack.FluentMVCTesting](https://github.com/TestStack/TestStack.FluentMVCTesting), and [NSubstitute](http://nsubstitute.github.io/).

1. Create a repository that provides access to content items using [generated classes](https://docs.kentico.com/13/developing-websites/generating-classes-for-xperience-objects.md). The repository only needs to contain the methods required by the application, not the data layer.

   ```csharp

   public interface IArticleRepository
   {
       IEnumerable<Article> GetArticle(int documentId);

       ...
   }

   ```

   ```csharp

   public sealed class ArticleRepository : IArticleRepository
   ...
       public Article GetArticle(int documentId)
       {
           return new DocumentQuery<Article>()
                           .WithId(documentId)
                           .Culture(cultureCode)
                           .OnSite(siteName)
                           .FirstOrDefault();
       }
   ...

   ```
2. Register the repository in your [dependency injection](https://docs.kentico.com/13/developing-websites/initializing-xperience-services-with-dependency-injection.md) provider or container. For example, in the application's _Startup.cs_ or _Global.asax.cs_ file.

   ```csharp

   builder.Register<ArticleRepository>().As<IArticleRepository>();

   ```
3. Inject the repository and any other dependencies in the controller constructor.

   ```csharp

   public class ArticleController : Controller
   {
       private readonly IArticleRepository articleRepository;

       public ArticleController(IArticleRepository repository)
       {
           articleRepository = repository;
       }
   ...

   ```
4. The controller contains an action that returns a view based on a specified article's ID, or _HttpNotFoundResult_ if the article does not exist.

   ```csharp

   // GET: Articles/Show/{id}
   public ActionResult Show(int articleId)
   {
       var article = articleRepository.GetArticle(articleId);
       if (article == null)
       {
           return HttpNotFound();
       }
       return View(article);
   }

   ```
5. Set up the controller test and create the controller with the required dependencies. The following example uses the [NUnit](http://nunit.org/) testing framework.

   ```csharp

   private IArticleRepository articleRepository;
   private ArticleController controller;

   [SetUp]
   public void SetUp()
   {
       // Allows creating of articles without accessing the database
       Fake().DocumentType<Article>(Article.CLASS_NAME);

       // Creates a mock article repository
       var article = TreeNode.New<Article>()
                                   .With(a => a.Fields.Title = "TestArticle")
                                   .With(a => a.SetValue("DocumentID", 1));

       articleRepository = Substitute.For<IArticleRepository>();
       controller = new ArticleController(articleRepository);
       articleRepository.GetArticle(1).Returns(article);
   }

   ```
6. Create individual test cases for the controller.

   ```csharp

   [Test]
   public void Show_WithoutExistingArticle_ReturnsHttpNotFoundStatusCode()
   {
       controller.WithCallTo(c => c.Show(2))
           .ShouldGiveHttpStatus(HttpStatusCode.NotFound);
   }

   ```
