已决定使用起订量等编写一些单元测试。其中有很多遗留代码 c#
(这超出了我的控制范围,因此无法回答其原因)
现在,当您不想访问数据库但仍然间接访问数据库时,您如何应对这种情况?
这是我整理的东西,它不是真正的代码,但给了你一个想法。
遇到这种情况你会如何处理?
基本上,在模拟接口(interface)上调用方法仍然会进行 dal 调用,因为该方法内部还有其他方法不属于该接口(interface)?希望很清楚
[TestFixture]
public class Can_Test_this_legacy_code
{
[Test]
public void Should_be_able_to_mock_login()
{
var mock = new Mock<ILoginDal>();
User user;
var userName = "Jo";
var password = "password";
mock.Setup(x => x.login(It.IsAny<string>(), It.IsAny<string>(),out user));
var bizLogin = new BizLogin(mock.Object);
bizLogin.Login(userName, password, out user);
}
}
public class BizLogin
{
private readonly ILoginDal _login;
public BizLogin(ILoginDal login)
{
_login = login;
}
public void Login(string userName, string password, out User user)
{
//Even if I dont want to this will call the DAL!!!!!
var bizPermission = new BizPermission();
var permissionList = bizPermission.GetPermissions(userName);
//Method I am actually testing
_login.login(userName,password,out user);
}
}
public class BizPermission
{
public List<Permission>GetPermissions(string userName)
{
var dal=new PermissionDal();
var permissionlist= dal.GetPermissions(userName);
return permissionlist;
}
}
public class PermissionDal
{
public List<Permission> GetPermissions(string userName)
{
//I SHOULD NOT BE GETTING HERE!!!!!!
return new List<Permission>();
}
}
public interface ILoginDal
{
void login(string userName, string password,out User user);
}
public interface IOtherStuffDal
{
List<Permission> GetPermissions();
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
}
有什么建议吗? 我错过了显而易见的事情吗? 这是无法测试的代码吗?
非常感谢您的任何建议。
最佳答案
就目前而言,BizLogin
是不可测试的,因为它直接实例化 BizPermission
,而 BizPermission
又实例化 PermissionDal
,然后命中数据库。
最好的解决方案是重构 BizLogin
以通过调用 factory (method) 来替换 BizPermission
的直接实例化。 ,或Dependency Injection 。从您的帖子中我不清楚您是否可以重构代码 - 如果可以,这是首选解决方案。
但是,如果重构不可行,您仍然可以尝试一些令人讨厌的技巧。这在 Java 中是可能的,我不太了解 C#,但是由于这两种语言非常相似,我想这在 C# 中也是可能的(尽管我无法填写确切的技术细节)。
您可以将 BizPermission
的已编译类文件替换为单元测试的不同模拟实现。这当然是有风险的,因为您必须确保替代实现不会混合到您的生产程序集中。它还需要一些困惑的类路径和东西。因此,只有在重构确实、绝对不可能的情况下才尝试这个。
如何用测试实现替换类文件
(使用 Java 术语 - 我希望它对于 C# 来说也足够清楚...)基本思想是运行时在类路径上查找类,并加载在类路径上找到的第一个合适的类定义。因此,您可以在单元测试源文件夹中创建 BizPermission 的模拟实现,该实现位于与原始包完全相同的包中并具有相同的接口(interface)。然后将其编译成例如test-classes
文件夹(而您的生产代码被编译到例如 classes
中)。现在,如果您设置测试类路径,使 test-classes
位于 classes
之前,则运行时将在运行测试时加载假的 BizPermission
类,而不是原始的,当 BizLogin
尝试实例化此类时。
使用工厂方法进行重构的示例
public class BizLogin
{
private readonly ILoginDal _login;
public BizLogin(ILoginDal login)
{
_login = login;
}
protected BizPermission getBizPermission()
{
return new BizPermission();
}
public void Login(string userName, string password, out User user)
{
var bizPermission = getBizPermission();
var permissionList = bizPermission.GetPermissions(userName);
//Method I am actually testing
_login.login(userName,password,out user);
}
}
在测试代码中:
public class FakeBizPermission implements BizPermission
{
public List<Permission>GetPermissions(string userName)
{
// produce and return fake permission list
}
}
public class BizLoginForTest
{
public BizLoginForTest(ILoginDal login)
{
super(login);
}
protected BizPermission getBizPermission()
{
return new FakeBizPermission();
}
}
这样,您就可以通过 BizLoginForTest
测试关键功能,只需对原始 BizLogin
类进行最少的更改。
另一种方法是注入(inject)完整的工厂对象,如 Jeff 的评论中所述。这需要更多的代码更改(可能包括 BizLogin
的客户端),因此更具侵入性。
请注意,此类重构的主要目标始终是允许单元测试,而不是为新设计的美观而获奖:-) 因此,最好从对现有代码进行最少的更改开始,这样您就可以通过测试覆盖功能。一旦测试到位,您就可以更有信心地进行重构,以获得更清晰、更好的设计。
关于unit-testing - 调用其他方法的模拟方法仍然会影响数据库。我可以避免吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3048330/