我有以下简化代码来描述我的问题:
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);
如何解决这个问题?
答案 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 Factory和Dependency 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,指出我之前的错误。