Writing my first C# MVC Unit Test
Tags: testing, csharp, dotnet, webdev
Published: 2021-05-18 14:29:12

After much research and thought, I'm diving in today with a very basic C# unit test, just to get my feet wet with Test Driven Development.  The purpose of this test, is to see if the home index will return 2 blogs.  It should be very simple, and I honestly rarely expect this test to break, but you've got to start somewhere!

# The setup.
I've Installed the xUnit extension into VS2019, and I'm adding a new xUnit Test Project to my solution.  Since this is for my blog project, I'll name it BlogTests, and put it in my project folder.  This creates a sample test with nothing in it at the moment, but we will fix that shortly.   

# Auto-generated "Tests"
One of the great things about VS2019 is that it can generate a "test" for you.  This test doesn't do anything, but it gives us a starting point.  Over in the main project's home controller, if we right click inside the index action (you can right click inside the overall class to make more than one test at once if needed) and go to "Create Unit tests" we get a lovely pop-up that gives us some options.  If its not already selected, make sure the Test Framework is xUnit.net 2.0, then select your test project, and `<New Test File>` before you hit OK.  I'm having it assert failure for all tests right now, since there isn't anything going on just yet.  

#Mocking the database

With EFCore, we actually don't have to make a Mock database, we will instead use the local database for testing.   It was rather difficult to get the Mock database working with EFCore, and in my research it turned out to be unnecessary.

# Writing the test itself

Once we have our Mock Database, we are ready to write the test.  Lets use a more descriptive name than "Index Test".  There are 3 parts considered standard to a test name:
1. The behavior being tested
2. The constraints
3. The expected behavior

I'm going to use "Index_ReturnsAViewResult_WithAPagedListOfFiveBlogs" as the name.  Its a mouthful, but if my test fails I will immediately know what failed.

There are now 3 parts to our test: Arrange, Act, and Assert.

## Arrange
This step involves putting the data in the correct spot, and performing any setup.
```
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseNpgsql(Connection.GetConnectionString(_configuration));
            var _dbContext = new ApplicationDbContext(optionsBuilder.Options);

            var controller = new HomeController(null,_dbContext);
```
The optionsBuilder is used to configure options for the ApplicationDbContext, which then is passed to the controller.  I did not pass the controller a logger at this point, since I am not using the logger to test.  Your options builder should take the same arguments that are used in startup.cs to set up your database.  

## Act.  
This part is very simple, just get a result from the controller
`            var result = await controller.Index(1); //passing page 1`
I'm passing a variable because my site is using X.PagedList to paginate (see [this post](https://dev.to/sbrevolution5/3-steps-to-paging-in-asp-net-blog-part-3-371m))

## Assert
 And here's the meat and potatoes, We're going to check that the type is ViewResult, and the model is based on an IPagedList.  Then finally, we ensure that the model only includes 5 items for this page. 
```
var viewResult = Assert.IsType<ViewResult>(result);
            var model = Assert.IsAssignableFrom<IPagedList<Blog>>(viewResult.Model);
            Assert.Equal(5, actual: model.Count());
```

To run a test, we use a series of "assert" statements.  If any of these fail, we will recieve a failure on a test.  This is where we're going to stop for now, but as a first experiment in testing, I'd call this a success.  

My main struggle at the moment is coming up with the right kind of tests.  Unlike working with a very math based console app, I don't feel like I have much to test within the controllers.  

What kinds of tests do you do with MVC apps?