This lecture was given at the Dec 2013 SDP conference and helps programmers get started with unit testing and spec testing. It talks about using the right tools for the job, including Visual Studio, ReSharper, SpecFlow, MSTest, NUnit, and others. It also gives some advice, including architectural advice, to help overcome common MVC testing hurdles.
You can download the code from my blog: http://noam.kfir.cc/blog/2013/12/21/sdp-testing-asp-net-mvc-web-apps
3. Motivation
Quality is everybody’s responsibility
The Challenge
As projects grow, complexity increases
The QA should be the last line of defense
Implementing and relying on automation
Tests
Help programmers take responsibility
Are an effective communication mechanism
Are essential to TDD workflows
4. Testing Challenges
Server and client code are often very different
C# vs. JS
Business logic vs. UI interaction
Single controlled vs. multi-platform environments
Different architectures
Classic web vs. SPA vs. hybrid apps
Multiple network/business topologies
Tooling and API limitations
Black box vs. white box testing
Singletons and other ailments
5. Our Focus
Visual Studio with a little help from ReSharper
ASP.NET MVC
Mainly TDD workflow, but not only
Solving real problems
And a few other tidbits
6. Visual Studio
Visual Studio encourages unit testing and TDD
Project Templates
Test Explorer
Navigation
Refactorings
8. Test Explorer
Built-in to Visual Studio since VS 2012
Designed for multiple workflows, including TDD
Run tests after build
Sorting, filtering, grouping
Uses test adapters to support any framework
MSTest + NUnit, Chutzpah, SpecFlow, and more
Test adapters run side-by-side
Can run JS and C# tests at the same time
Not just for unit tests: Web tests, Coded UI tests…
10. ReSharper
Visual Studio’s little helper – fully integrated
Visual Studio has improved, but ReSharper
makes it better!
Provides more tools for effective unit testing
Test Runner
Easier navigation
Enhanced code completion
Many refactorings, and more flexible
12. Types of Tests
Unit tests
Test function inputs and outputs
High-resolution white box tests
Specification tests
Verify that the app behaves according to specs
Often support high-level natural language tests
Web tests
Part of MSTest
Runs as an HTTP client, similar to Fiddler
Has a test designer and supports coded tests
13. Unit Test Frameworks
MSTest (.NET)
Inspired by an early version of NUnit
Easy to migrate to NUnit (eventually)
Integrates well with TFS
NUnit (.NET) via NUnit test adapter
Feature-rich
Provides fluent assertions
More widespread than MSTest
Jasmine (JavaScript) via Chutzpah test adapter
Very easy to learn behavior-driven framework
15. SpecFlow
Specification test framework
Tests scenarios and use cases
Uses Gherkin
High-level natural language tests
Supports Hebrew!
Programmers translate natural language to
code
Can run on top of various unit test frameworks
17. Web Tests
Marketed as a testing tools for testers
But very effective for programmers as well
Provide a mechanism for server integration
tests
Client-agnostic
Especially useful for testing HTTP-compliant
and RESTful sites
19. Common Issues
Isolating Controller dependencies
The Repository pattern
Working with databases
Crossing layer boundaries
Testing routes
Testing views
20. Isolating Controller Dependencies
? We need to isolate our unit tests (with
Moq, TypeMock, etc.), but controllers often
depend on singletons or other global
services
Invert your dependencies
Custom ControllerFactory or IoC container
Wrap singletons
Make the test smaller
Not every Controller action has dependencies
Rely on conventions
Use models and model binding and provide your
own
21. The Repository Pattern
? The Repository pattern is an effective
pattern for managing entities and
databases, but repositories are difficult to
test and mock
Repositories and controllers belong to different
application layers
Test them separately
Controllers should rely on mockable interfaces
Mock intensively
Beyond that, boils down to the database
question
22. Databases
? Using a real database for unit tests violates
the isolation requirement, but sometimes
you really need to test with real (or fake)
data
Actually, you usually don’t
If you do, consider integration or spec tests
Options:
Mock repository or database replies
Create fake databases, possibly with embedded or
in-memory databases (ORMs make this relatively
easy)
Constrain your tests to the right application layer
23. Crossing Layer Boundaries
? It’s not always practical to write unit
tests, but integration and spec tests can be
more fragile
Both types of tests are useful and important –
they target different types of issues and have
different goals
Make sure you test a single layer
Regardless of the type of test
Spec and integration tests are also isolated, just at a
different level
Make sure your unit tests focus on a single layer as
well
24. Testing Routes
? Managing routes, removing conflicts, and
verifying routes find the right controllers is
difficult
Are routes code or configuration?
Consider integration or web tests
Do you test configuration?
If you have custom routes or resolve routes
with custom mechanisms, test (just?) those
mechanisms
25. Testing Views
? There’s no real way to test views, which is
especially problematic when they have logic
Views are output, rendered, and should not
have to be tested with unit tests
If your views are using ifs or other control flow
code constructs, consider moving the code to
your controller and model
If you really need to test it, use web or UI
tests, but keep in mind that they’ll probably be
fragile
26. Summary
ASP.NET MVC is designed to be testable
Visual Studio supports almost every workflow
Tooling is vital to successful testing
(ReSharper…)
Many testing problems can be solved by
improving the architecture
Do not rely just on the QA (or your customers)
to test your app
Take responsibility for your code