It is widely known that you can't unit test Asp.Net applications. That is, unless you use the new MVC model, which is still under construction. Even with MVC, you can't test the view, and you usually don't have to, since you make it as simple as possible.
There are numerous reasons for that. First, you just can't fake any depedencies. You can't do dependency injection: the View's constructor should be parameterless (unless you provide your own PageFactory, but that's a different story), and you can't avoid calling the ProcessRequest method. In short, you are not in the control. Once you have created a page, it's in the hands of the Asp.net runtime, which mercifully lets you handle a couple of events, but only in the production code. Second, you just can't take an instance and do various things you want to test.
That's not mentioning the fact that everithing's happening in a different process.
As a result, people tend to move all code to other places, where it can be tested. I do agree that often it's good to make your view thinner. But you have to be careful and not move the code that belongs there. For example reading the control values from another class makes it too coupled to the view, and doesn't improve testability. On the other hand, sometimes you can't avoid complicated event handlers coupled to page controls. For example, consider a handler of the repeater's RowDataBound event, where you have to retrieve the row's grandparent control, find a particular label inside it, and assign a particular value to it. You can't test it, so you have to manually debug it.
With Ivonna, it's different. First, you run all your code in the same process. Second, you can hook into the running code and change stuff to meet your needs. And third, you have TypeMock at your hands, so that you can mock the stuff you can't change manually.
You still have to design for testability though. It won't work with dropping a table from the Server Explorer. You'll have to deal with SqlClient intrinsics in order to figure out which method to mock and what to return. So you'll have to improve your design skills a bit.
Consider displaying a set of database records. In a strongly coupled version you can use an ObjectDataSource, for example, and hardcode some model class as its data source. The model itself doesn't know about the Web and resides in a separate project. That's why it is convenient to mock it: we mock the boundary between our layers. We could mock the Select method of the ObjectDataSource, but then we wouldn't be sure that it calls our model, so part of our UI layer would remain untested.
So, what we do it setup a mock controller for the model class and setup the return values for its select method, all that before we get the page. Then investigate the table and test the values. Still, it looks like an integration test. So, we can test two things: that the grid's is set to the ID of our ObjectDataSource, and that ObjectDataSource retrieves the correct values. How do we do that?
- Handle the PageInit method
- Inside the handler, evaluate the DataSourceID property of our grid. That's test one.
- Still inside the handler, find the ObjectDataSource instance using FindControl()
- Manually invoke the Select method and check the result.
Handling the Init event can be done via the WebRequest object. In cases like that, it is not enough to call session.GetPage() to retrieve a page. You have to prepare the request, get the responce using session.ProcessRequest(), and get the Page property.
The decoupled scenario is, as always, more complicated. You might want to assign the data source directly in your code, but you probably have an interface that incapsulates your model. However, whatever architecture you are using, you still have to instantiate a real model class and wire up all your dependencies. So, you can either mock the model class as in the previous example, or mock the class that does the actual instantiation and return a fake model instance. The second option lets you use a fake model provided by TypeMock, any other mock framework, or created manually, while the first option still uses the real model class with all relevant methods intercepted and providing mock results.