Moq:如何模拟一个不可见的类?

时间:2010-08-27 12:53:47

标签: interface moq

我有以下简化代码来描述我的问题:

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

在数据访问层中使用的是这样的:

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

userlogic类定义如下:

public class UserLogic
{
    private IData da;

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

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

userlogic使用 MyUSer 类,该类仅在内部可见。

我想使用Moq来模拟对 dataaccess 图层的调用。但是因为我无法从我的单元测试代码(这是设计的)访问MyUser类,我不知道如何设置moq?

Moq代码应该是这样的:

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

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

如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

你不能用

da.GetUserById<IMyUser>(id);

而不是

da.GetUserById<MyUser>(id);

答案 1 :(得分:2)

让我简单介绍一下Sjoerd的回答。您遇到的问题是由于无法从测试程序集中访问MyUser类型。使用InternalsVisibleTo程序集属性可以轻松解决该问题。

但我建议重新考虑您的设计并摆脱IMyUser界面,而只使用MyUser类(应该是公共的)。通常,您将服务放在接口后面,而不是实体。是否有充分理由提供IMyUser的多个实现?

看看这个实现有多清洁:

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; }
}

如果你坚持使用IMyUser接口及其内部实现,还有另一种解决方案。如果我正确地推断出IData.GetUserById<T>的内容,你现有的解决方案是这样的:

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;
    }
}

上面的代码略微违反了SRP(warning, PDF)并且混合了两个职责 - 从持久存储中检索实体并创建实体的实例。不仅如此,它还将创作责任放在界面上,这更糟糕。

使用Abstract FactoryDependency Injection(PDF)模式解除这些责任将导致更清洁的设计,不会遇到与以前相同的问题。

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);
}

答案 2 :(得分:1)

如果我想隐藏功能但让它可以测试,我会将函数声明为internal,然后在文件的顶部添加[assembly: InternalsVisibleTo("MyAssemblyName")]属性,其中{{1是您要授予访问权限的单元测试程序集。谢谢,Stef,指出我之前的错误。