tags:

views:

80

answers:

3

I've the following simplified code which describes my problem:

public interface IMyUser
{
    int Id { get; set; }
    string Name { get; set; }
}

Which is used in the dataccess layer like this:

public interface IData
{
    T GetUserById<T>(int id) where T : IMyUser, new();
}

The userlogic class is defined as follows:

public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public IMyUser GetMyUserById(int id)
    {
        return da.GetUserById<MyUser>(id);
    }
}

The userlogic uses a MyUSer class which is only visible internally.

I want to use Moq to mock the call to the dataaccess layer. But becuase I cannot access the MyUser class from my unit test code (which is as designed) , I don't know how to setup moq?

The Moq code should be something like:

var data = new Mock<IData>();
data.Setup(d => d.GetUserById<MyUser ???>(1)).Returns(???);

var logic = new UserLogic(data.Object);
var result = logic.GetMyUserById(1);

How to solve this?

+2  A: 

Can't you use

da.GetUserById<IMyUser>(id);

instead of

da.GetUserById<MyUser>(id);
Sjoerd
I should have read that closer. I believe you are correct. Always good to program to an interface and actually use it. :)
Dave
That actually will not work as it will violate the `new()` constrain on `GetUserById<T> where T: IMyUser, new()`
Igor Zevaka
+1  A: 

If I want to hide functionality but let it be testable, I'll declare the functions as internal, and then at the top of the file I add the [assembly: InternalsVisibleTo("MyAssemblyName")] attribute, where MyAssemblyName is the unit test assembly that you want to grant access to. Thanks, Stef, for pointing out my previous mistake.

Dave
Isn't it the other way around ? This should be added to the assembly which contains the internal types (datalayer). This is described at the moq website: [assembly:InternalsVisibleTo("DynamicProxyGenAssembly2,PublicKey=**]
Stef
@Stef! YES! Sorry, I typed that incorrectly. Fixing my post now. Thank you.
Dave
+1  A: 

Let me just expand on Sjoerd's answer. The problem you are facing is due to not being able to access MyUser type from the test assembly. That problem is easily fixed with InternalsVisibleTo assembly attribute.

I would however recommend to rethink your design and get rid of IMyUser interface and instead just use MyUser class (which should be public). Normally you put services behind interfaces, not entities. Are there any good reasons for providing multiple implementations of IMyUser?

Have a look at how much cleaner this implementation is:

public interface IData
{
    MyUser GetUserById(int id);
}

public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public MyUser GetMyUserById(int id)
    {
        return da.GetUserById(id);
    }
}

internal class MyUser {
    int Id { get; set; }
    string Name { get; set; }
}

There is another solution, if you insist on having IMyUser interface and its internal implementation. Your existing solution, if I infer the contents of IData.GetUserById<T> correctly, goes something like this:

public class UserData : IData {
    T GetUserById<T>(int id) where T : IMyUser, new(){
       T returned = new T();
       //fill in properties
       returned.Name = "test";
       return returned;
    }
}

The above code is a slight violation of SRP(warning, PDF) and mixes two responsibilities - retrieving an entity from persistent storage and creating an instance of the entity. Not only that, it also puts the creation responsibility on the interface, which is even worse.

Decoupling those responsibilities using Abstract Factory and Dependency Injection(PDF) patterns will lead to much cleaner design that does not suffer from the same problem as before.

public interface IMyUserFactory {
    IMyUser Create();
}

public interface IData
{
    IMyUser GetUserById(int id);
}

internal MyUserFactory : IMyUserFactory {
   public IMyUser Create() {return new MyUser();}
}

internal class UserData : IData {

    IMyUserFactory m_factory;
    public UserData(IMyUserFactory factory) {
       m_factory = factory;
    }

    public IMyUser GetUserById(int id) {
       IMyUser returned = m_factory.Create();
       //fill in properties
       returned.Name = "test";
       return returned;
    }
}

//and finally UserLogic class
public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public IMyUser GetMyUserById(int id)
    {
        return da.GetUserById(id);
    }
}

//The test then becomes trivial
[TestMethod]
public void Test() {
  var data = new Mock<IData>();
  data.Setup(d => d.GetUserById(1)).Returns(new Mock<IMyUser>().Object);

  var logic = new UserLogic(data.Object);
  var result = logic.GetMyUserById(1);
}
Igor Zevaka
Hello Igor, thanks for your extended answer. (BTW my example code in this question was simplified, the actual design is even more complicated.) But your solution sounds very good.
Stef