Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test ASP.Net MVC POST Controller with Moq and Nunit

I want to write a unit test for testing that a new item is created, without actually persisting it to the database. I'm not very familiar with Moq, or mocking in general.

Here's the controller code:

// POST: api/MoviesAPI
[ResponseType(typeof(MovieDTO))]
[Route("api/movies/post")]
public IHttpActionResult PostMovies(MovieDTO movie)
{

    // Validation
    if (String.IsNullOrWhiteSpace(movie.Title))
    {
        return Ok("Movie Title is required");
    }

    if (movie.GenreIds == null || movie.GenreIds.Count == 0)
    {
        return Ok("A new Movie requires at least one genre to be selected");
    }

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    // /Validation

    //// Create Movie type object to add
    var newMovie = new Movies()
    {
        Title = movie.Title,
        Released = movie.Released
    };

    using (var db = new MoviesContext())
    {
        // Link genres from DB to the new Movie
        newMovie.Genres = new List<Genres>();
        foreach (var g in movie.GenreIds)
        {
            var genreToAdd = db.Genres.FirstOrDefault(x => x.GenreId == g);
            newMovie.Genres.Add(genreToAdd);
        }

        // Try to insert the new Movie
        try
        {
            db.Movies.Add(newMovie);
            db.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            return BadRequest(ex.Message);
        }


        return Ok(movie);
    }
}

And my incomplete test:

[TestMethod]
public void Insert_NewMovie_IsSuccessful()
{
    //var sut = new Mock<MoviesAPIController>();
    var sut = new MoviesAPIController();
    var model = new MovieDTO();

    //sut.PostMovies(model);

    // TODO: Assert
}
like image 213
Jonathan Opperman Avatar asked Jan 07 '23 13:01

Jonathan Opperman


1 Answers

Along with the suggested comments you should have a look at the following resource

Unit Testing Controllers in ASP.NET Web API 2

C# Writing unit tests with NUnit and Moq

Writing unit tests with NUnit and Moq

That last link is just the Google search I used to get the previous ones. :)

If you take the time and review the links provided you will have a better idea of how to write a unit test for testing that a new item is created, without actually persisting it to the database

The following example is modeled from the link above and modified to show how you can do it based on the provided code.

Here is a simplified controller where all your db code has been separated out into it's own concern.

[RoutePrefix("api/movies")]
public class MoviesAPIController : ApiController {

    IMovieRepository repository;

    public MovieController(IMovieRepository repository) {
        this.repository = repository;
    }

    [HttpGet]
    [Route("")]
    [ResponseType(typeof(MovieDTO))]
    public IHttpActionResult Get(int id) {
        var movie = repository.GetById(id);
        if (movie == null) {
            return NotFound();
        }
        return Ok(movie);
    }

    // POST api/movies
    [HttpPost]
    [Route("")]
    [ResponseType(typeof(MovieDTO))]
    public IHttpActionResult PostMovies(MovieDTO movie) {

        // Validation
        if (String.IsNullOrWhiteSpace(movie.Title)) {
            return BadRequest("Movie Title is required");
        }

        if (movie.GenreIds == null || movie.GenreIds.Count == 0) {
            return BadRequest("A new Movie requires at least one genre to be selected");
        }

        if (!ModelState.IsValid) {
            return BadRequest(ModelState);
        }

        repository.Add(movie);
        return CreatedAtRoute("DefaultApi", new { id = movie.Id, title = movie.Title }, movie);
    }

    public IHttpActionResult Delete(int id) {
        repository.Delete(id);
        return Ok();
    }

    public IHttpActionResult Put(MovieDTO movie) {
        // Do some work (not shown).
        return Content(HttpStatusCode.Accepted, movie);
    } 
}

And here is a sample test for the POST, again modified based on you original example.

[TestMethod]
public void PostMethodSetsLocationHeader()
{
    // Arrange
    var mockRepository = new Mock<IMovieRepository>();
    var controller = new MoviesAPIController(mockRepository.Object);
    var model = new MovieDTO()
    {
        Title = "Mad Max: Fury Road",
        Released = DateTime.Parse("15 May 2015"),
        GenreIds = new List<int>(){ 1 }
    };

    // Act    
    IHttpActionResult actionResult = controller.PostMovies(model);
    var createdResult = actionResult as CreatedAtRouteNegotiatedContentResult<MovieDTO>;

    // Assert
    Assert.IsNotNull(createdResult);
    Assert.AreEqual("DefaultApi", createdResult.RouteName);
    Assert.AreEqual(model.Title, createdResult.RouteValues["title"]);
}
like image 118
Nkosi Avatar answered Jan 09 '23 03:01

Nkosi