+3  A: 

You may take a look at a sample ASP.NET MVC 2.0 project structure I wrote. It presents some concepts that might get you started on unit testing your controller logic and database. As far as database testing is concerned it is no longer unit tests but integration tests. As you will see in my sample I am using NHibernate which allows me to easily switch to a sample SQLite database that is re-created for each test fixture.

Finally doing unit tests in ASP.NET MVC could be a pain without proper separation of concerns and abstractions and using mocking frameworks and frameworks like MVCContrib.TestHelper could make your life easier.

Here's a preview of how your controller unit test might look like.


UPDATE:

As a response to the comments I think that programming is a concrete task and it is difficult to give an ultimate answer as to how do I unit test my complex business application. In order to be able to test a complex application coupling between the layers should be as weak as possible which could be achieved with interfaces and abstract classes. I agree though that achieving such a weak coupling in a complex application is not a trivial task.

I could give you an advice: if the whole TDD concept is difficult to understand and you see no benefits in it then this is OK. Nobody can prove that TDD is beneficial in all situations. Just try to design your application in such a way that each class has a single responsibility. If you find yourself doing validation of input, SQL data access and exception handling in the same class then you are doing it wrong. Once you achieve this separation you will see that unit testing becomes much easier and you could even come to a stage when unit tests will drive your development :-)

As far as unit testing a JsonResult with MvcContrib.TestHelper this is a concrete question to which I give a concrete answer:

public class MyModel
{
    public string MyProperty { get; set; }
}

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return Json(new MyModel { MyProperty = "value" });
    }
}

And the test:

[TestMethod]
public void HomeController_Index_Action_Should_Return_Json_Representation_Of_MyModel()
{
    // arrange
    var sut = new HomeController();

    // act
    var actual = sut.Index();

    // assert
    actual
        .AssertResultIs<JsonResult>()
        .Data
        .ShouldBe<MyModel>("")
        .MyProperty
        .ShouldBe("value");
}
Darin Dimitrov
I have a separate SQL database that gets used for the unit tests, and I believe I can use EF4 with sqllite databases so that's not the issue. The issue is that I already have separatation of concerns, complete with EF handling the database layer, my business layer, and my application layer (the MVC). The only way to facilitate testing is to spread my business layer apart, which makes 2 REALLY thin layers, making the query path being controller->Service->DTO->EF, where all the services aren't doing much for most actions, which makes maintenance a pain.
KallDrexx
Also the testhelper doesn't seem to help with testing jsonresults. I also can't see how the code you linked to helps with object dependencies and more complicated business logic (for example adding a new verification rule or object dependency will cause all of your existing tests to fail). I understand the Todd architectures, I just find it hard to implement in non-trivial scenarios.
KallDrexx
Please see my update about unit testing JsonResult.
Darin Dimitrov
I think I am beginning to see where my confusion lies. I guess I am combining the repository layer and business/service layers together and having trouble trying to figure out how to separate them out without repeating methods between the two (i.e. making both the repository and business layer have a GetProjectByID() method).
KallDrexx
+2  A: 

It sounds like the problem is not with unit testing, but with your tooling/platform. I come from a Java background, so I don't have to re-write my queries just to satisfy unit tests.

Testing Json is a pain in the you know what. If you don't want to test it, don't ;) Testing is not about 100% coverage. It's testing the stuff that really needs to get tested. You are way more likely to get a bug inside of a complicate if expression than adding things to a map, and then getting it converted to json.

If you test that the map is getting created properly... there is a really good chance that the json is also correct. Of course, it won't always be, but you'll know that once you run it and it works. Or not. It's really that simple.

I can provide a real comment on the 3rd problem though. You really don't want to create massive object graphs. But there some ways to do it.

Create a singleton called ObjectMother. Have methods like ObjectMother.createProject(). Inside, you create the dummy project instance. That way, 12 tests can use this method, and then you only have to change it in one spot. Remember, test code needs to be refactored!

Also, you might look into something like dbUnit. Well, that's what it's called in the Java World. The idea is that before every test is run, it puts the database in a known state. And it does this automatically in the setup/teardown of your tests. This means your test code can immediately start testing. The actual dummy test data is in a script or xml file.

I hope that helps.

egervari
After re-thinking some things I think I think I was doing my tests incorrectly before and that was causing the incorrect linq linking. I was basing the linking straight from IDs (i.e. prj.UserId = User.Id) rather than just doing it in the OO way (prj.User = User), thus the linq queries would not work as written and had to be rewritten for unit tests. I think if I solve that I can actually unit test very simply and it will lessen my confusion about TDD quite a lot.
KallDrexx
+1  A: 

As far as testing your AJAXified views goes I would suggest that you try out a testing framework like WatiN.

With this you can do the following (example from their site).

[Test]
public void SearchForWatiNOnGoogle()
{
 using (var browser = new IE("http://www.google.com"))
 {
  browser.TextField(Find.ByName("q")).TypeText("WatiN");
  browser.Button(Find.ByName("btnG")).Click();

  Assert.IsTrue(browser.ContainsText("WatiN"));
 }
}
Matt