Moq : How to mock a class which is not visible?
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:
publi开发者_开发问答c 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?
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);
}
Can't you use
da.GetUserById<IMyUser>(id);
instead of
da.GetUserById<MyUser>(id);
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.
精彩评论