LyraLearn AI Learning Platform
Exams
← Module 8 Β· Testing .NET Applications
🎧 Listen

Testing Data Access and Controllers

Two parts of an MVC app resist plain unit testing: code that talks to the database, and code that talks to the HTTP pipeline. Both have honest testing strategies β€” and popular dishonest ones worth recognizing.

Testing data access: three tiers of realism

Repository and query code ultimately produces SQL, and the bugs live in that translation β€” so the closer your test database is to real SQL Server, the more bugs the test can catch.

Don't mock DbSet directly β€” it's fragile, and it lies about query translation. The pragmatic split: business rules get unit tests with a mocked repository interface; the repository itself gets a handful of real-database tests.

Controller tests: thin, and testing YOUR logic

A controller is instantiable like any class: new it up with mocked services, call the action, assert on the result:

var result = controller.Details(42) as ViewResult;
Assert.IsType<CandidateDetailsViewModel>(result.Model);

Worthwhile assertions: the right result type (ViewResult vs RedirectToActionResult vs 404), the model handed to the view, and branching β€” invalid input redisplays the form, success redirects (the PRG pattern from the MVC module). Not worthwhile: re-verifying every mock call the action makes. If there's nothing to assert but mock interactions, the controller is appropriately thin β€” stop testing it.

The ModelState pitfall

The single most common controller-test mistake: model binding and validation don't run when you call an action directly, so ModelState.IsValid is always true β€” even for a ViewModel violating every [Required] attribute. To test the invalid branch, poison ModelState yourself:

controller.ModelState.AddModelError("Ssn", "Required");
var result = controller.Create(emptyModel);
Assert.IsType<ViewResult>(result);   // redisplays, doesn't save

Then verify the service was never called. Testing that the attributes themselves are present is a separate, cheaper check β€” read them via reflection, or lean on an integration test that posts a real form through the pipeline. Know which of the three you're testing: your attributes, your branch logic, or the framework's binding β€” and don't write a test that accidentally tests none of them.

🧠 Quiz yourself on this lesson →

Ask the AI Tutor

Grounded in the course lessons β€” it cites its sources and says when it doesn't know.