views:

28

answers:

2

I am working through the book Professional ASP.NET MVC 2 and I am trying to get the unit testing in chapter 1 to work correctly; howver, I am getting some very strange errors.

There are two projects in the solution: NerdDinner, and NerdDinner.Tests.

In the NerdDinner Project I have the following interface:

IDinnerRepository.cs

//...
namespace NerdDinner.Models
{
     interface IDinnerRepository
     {
     //...
     }
}

Also in the NerdDinner project, I have the following class:

//...
using NerdDinner.Models;
//...
namespace NerdDinner.Controllers
{
     public class DinnersController : Controller
     {
     IDinnerRepository dinnerRepository;
     // Default constructor
     public DinnersController() : this(new DinnerRepository()){} // DinnerRepository is another concrete implementation of IDinnerRepository
     //Test constructor
     public DinnersController(IDinnerRepository repository) {
     dinnerRepository = repository;
     }
     }
}

In the NerdDinner.Tests project, I have the following concrete implementation of IDinnerRepository:

//...
using NerdDinner.Models;
//...
namespace NerdDinner.Tests.Fakes
{
     class FakeDinnerRepository : IDinnerRepository
     {
     //...
          public FakeDinnerRepository(List<Dinner> dinners)
          {
          //...
          }
     //...
     }
}

Now for the actual unit test (in NerdDinner.Tests)

using NerdDinner.Controllers;
//...
using NerdDinner.Models;
using NerdDinner.Tests.Fakes;
namespace NerdDinner.Tests
{
     [TestClass]
     public class DinnersControllerTest
     {
          List<Dinner> CreateTestDinners()
          {
          //...
          }
          DinnersController CreateDinnersController()
          {
          return new DinnersController(new FakeDinnerRepository(CreateTestDinners()));
          }
     }
}

And now for the actual problem: In the method CreateDinnersController in the class DinnerControllerClass, I am getting the following error:

DinnersController.DinnersController(NerdDinner.Models.IDinnerRepository repository) (+ 1 overload(s)) Error: The best overloaded method match for 'NerdDinner.Controllers.DinnersController.DinnersController(NerdDinner.Models.IDinnerRepository)' has some invalid arguments.

It gives me the option to create a constructor stub in DinnersController. It generates the following code:

private global::NerdDinner.Tests.Fakes.FakeDinnerRepository repository;
//...
public DinnersController(global::NerdDinner.Tests.Fakes.FakeDinnerRepository repository)
{
     // TODO: Complete member initialization
     this.repository = repository;
}

Even after generating that code, I still get the same error. But why should I even need that code anyway? As far as I can tell, I am doing everything correctly.

Can anybody help me figure out what is going on here?

Edit The generated code is giving the following error:

The type or namespace 'Tests' does not exist in the namespace 'NerdDinner' (are you missing any assembly reference?)

A: 

The last error you're getting is due to there being no reference from the production code to the test code - but that's appropriate. You don't want that extra constructor.

Instead, you need to find out why the existing constructor taking an IDinnerRepository isn't being used. Are you sure you only have one interface called IDinnerRepository? If you go to the FakeDinnerRepository source, go to the declaration, put the cursor in IDinnerRepository and hit F12 (go to definition) does it go to the right place?

If you add a new member to IDinnerRepository (just for the sake of testing: void Foo(); would be fine) does it cause both the production and fake implementation to fail to compile?

Jon Skeet
Yes, F12 took me to the one IDinnerRepository in NerdDinner.Models. When I added void foo(), it forced to me to all that method to all concrete implementations.
npsken
+1  A: 

From what you've shown the IDinnerRepository interface is not public meaning that it is not visible from your unit test. I would recommend you making it public as I suspect you have two different interfaces : one defined in the unit test and one in your project which conflict. Also I would recommend you to avoid relying on Visual Studio generate all the crap reflection code in order to test private and internal members.

Darin Dimitrov
I made the interface public but I am still getting the same error.Correction: The error is not longer there. Thanks!
npsken
@KPthunder: If the interface wasn't public before, how were you managing to implement it in your test project? That's why I suggested to check where the "go to definition" would take you...
Jon Skeet
@Jon Skeet: I just typed out the code and it worked. The IDE was putting in the methods for me after I typed the name of the interface. I don't currently have resharper installed on this computer, do you think resharper would have found this?
npsken
@KPthunder: I'm not sure, but you should take note of what the IDE is doing for you - the interface must have got into the test project *somehow*, and you should be very wary of that.
Jon Skeet
@Jon Skeet: I don't think the interface is in the test project though, because when I pressed F12 when it wasn't public it brought me to the interface in the non-test project.
npsken