.net 单元测试和对象范围-如何测试私有/内部方法等?
假设我有一个项目是类库。我在那个库中有一个类,这个类有一些只在类内部使用的方法。像这样:.net 单元测试和对象范围-如何测试私有/内部方法等?,.net,unit-testing,nunit,.net,Unit Testing,Nunit,假设我有一个项目是类库。我在那个库中有一个类,这个类有一些只在类内部使用的方法。像这样: public class MyClass { public void MyPublicMethod { int k // do something ... int z = MyInternalMethod(k); // do something else ... } internal int MyInternalMethod(int i) {
public class MyClass
{
public void MyPublicMethod
{
int k
// do something ...
int z = MyInternalMethod(k);
// do something else ...
}
internal int MyInternalMethod(int i)
{
// do something ...
}
}
现在我想为这些方法编写单元测试。我将创建一个“单元测试”项目,从中引用nunit并编写如下内容
[TestFixture]
public class UnitTests
{
private MyClass myClass;
[SetUp]
public void SetupTest
{
myClass = new MyClass();
}
[Test]
public void TestMyInternalMethod
{
int z = 100;
int k = myClass.MyInternalMethod(z); //CAN NOT DO THIS!
Assert.AreEqual(k, 100000);
}
[TearDown]
public void TearDown
{
myClass = null;
}
}
当然,我不能这样做,因为MyInternalMethod的作用域。处理这种情况的正确方法是什么?您可以通过使用使内部构件对某些组件可见。我喜欢将我的单元测试与它们正在测试的单元测试放在同一个类中。这有两个优点,第一个是它解决了您遇到的问题,第二个是您永远不会丢失或忘记它们,因为如果它们位于单独的部件中,往往会出现这种情况 并不是每个人都同意这种方法(我之前问过这个问题),但到目前为止,我还没有发现或向我指出其中的任何缺陷。我已经用这种方式做了4到5年的单元测试了
#if UNITTEST
using NUnit.Framework;
#endif
public class MyBlackMagic
{
private int DoMagic()
{
return 1;
}
#if UNITTEST
[TestFixture]
public class MyBlackMagicUnitTest
{
[TestFixtureSetUp]
public void Init()
{
log4net.Config.BasicConfigurator.Configure();
}
[Test]
public void DoMagicTest()
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
Assert.IsTrue(DoMagic() == 1, "You are not a real magician!");
}
}
#endif
}
我猜这取决于你对单位的理解,对吧?我通常为可访问的接口编写单元测试,而忽略私有的东西。我曾与一些人合作过,他们会为单元测试访问提供私有事物保护(java)。我真的不喜欢这种方法,因为它牺牲了类设计的清洁度来进行测试访问
就我个人而言,我只是避免编写任何真正复杂的私有方法。还有其他方法可以封装您不想公开的行为,同时还可以让您自己测试应该隐藏的东西。我认为在完美封装和可测试性之间有一个折衷。完美的封装是很难实现的,让自己对类有更多的了解通常更有益。这可能是有争议的。Visual Studio可以为您生成私有访问器。查看MSDN。我相信VS2005只是生成了私有访问器类并将其添加到单元测试项目中。所以,当情况发生变化时,你必须使它们再生。但是,VS2008生成一个私有访问器程序集,并使其可用于单元测试项目。你用的是NUnit,但我觉得应该没问题。过来看。通过这种方式,您可以使实际代码免受任何测试相关代码和/或黑客攻击 过去,我在与被测类相同的命名空间和程序集中创建了测试装置,以测试内部方法。我不是说是否应该测试内部方法。实际上,您可以测试它们,然后在以后重构 我还创建了分部类来测试私有方法,并在整个部分(在它自己的文件中)使用了编译器指令。再说一次,不要说这是最好的,但有时你需要前进 在构建时,我们可以在调试或发布模式下运行单元测试,如果需要,我们可以从任何一个构建中剥离测试代码,因此将测试代码与待测试代码放在一起是无害的;如果有什么区别的话,它的参数类似于代码和数据一起=对象或对象,文档注释=文档对象。换句话说:代码和数据以及测试和文档注释一起=内聚单元
构建过程中的额外时间可以忽略不计。我只是测试公共方法(不,我不关心覆盖率指标,我关心的是有效的功能)
注意,如果公共方法不使用内部方法,那么内部方法就不需要存在 许多人会说,不应该测试内部方法,而应该通过公共API测试它们。无论如何,如果您真的想访问这些私有成员,您可以使用反射。我使用了几种方法来实现这一点。我已经对我的私有方法进行了保护,这样我就可以在单元测试中从类继承,并创建一个“helper”类来测试这些方法。另一个是反射。在我看来,反射是有问题的,但它确实违背了类的测试设计。这是我所说内容的简化版本
public static class ReflectionHelper
{
public static object RunStaticMethod<TInstance>(string methodName, params object[] methodParams)
{
var methodType = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
return RunMethod<TInstance>(null, methodName, methodType, methodParams);
}
public static object RunInstanceMethod<TInstance>(this TInstance instance, string methodName, params object[] methodParams)
{
var methodType = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
return RunMethod<TInstance>(instance, methodName, methodType, methodParams);
}
private static object RunMethod<TInstance>(object instance, string methodName, BindingFlags methodType, params object[] methodParams)
{
var instanceType = typeof(TInstance);
var method = instanceType.GetMethod(methodName, methodType);
if (method == null)
{
throw new ArgumentException(string.Format("There is no method '{0}' for type '{1}'.", methodName, instanceType));
}
var result = method.Invoke(instance, methodParams);
return result;
}
}
你就这么用吧
var user = new User { FirstName = "Peter", LastName = "Gibbons" };
var name = user.RunInstanceMethod("GetPrettyName");
Assert.That(name, Is.EqualTo("Peter Gibbons"));
var id = ReflectionHelper.RunStaticMethod<User>("GetSystemId", "tester");
Assert.That(id, Is.EqualTo(13));
var user=new user{FirstName=“Peter”,LastName=“Gibbons”};
var name=user.RunInstanceMethod(“GetPrettyName”);
断言(名称为.EqualTo(“彼得·吉本斯”);
var id=ReflectionHelper.RunStaticMethod(“GetSystemId”、“tester”);
断言(id,Is.EqualTo(13));
有两种情况:要么您的私有方法从某个公共方法调用,在这种情况下,您可以通过该方法测试它们。或者,它们不会从某个公共方法中被调用,在该方法中它们根本无法被调用,它们是死代码,应该被删除,而不是测试
请注意,如果您正在进行TDD,私有方法只能通过从公共方法中提取它们而存在,在这种情况下,它们已经被自动测试。糟糕,糟糕,糟糕-请不要这样做。但是您需要从您拥有的任何项目中引用nunit.framework。我不确定这是否会产生诸如更长的构建时间、更大的可执行文件之类的后果,但这似乎有点笨拙。@Corehpf-我们这里不讨论宗教。如果您有反对某件事的论点,请提供它。@Evgeny-it从未引起任何问题,在构建时间上也没有明显的差异。如果需要,可以轻松关闭UNITTEST预编译器指令并删除引用。或者,如果它是一些非常需要的东西,您可以在VS.Net中创建不同的构建配置……然后您使用模拟框架,还必须部署该库。如果您想使用类似IoC容器的任何东西,您必须确保您没有在生产中使用测试配置。条件编译——不寒而栗。我敦促其他人不要这样做!同意。我将使用InternalsVisibles来
var user = new User { FirstName = "Peter", LastName = "Gibbons" };
var name = user.RunInstanceMethod("GetPrettyName");
Assert.That(name, Is.EqualTo("Peter Gibbons"));
var id = ReflectionHelper.RunStaticMethod<User>("GetSystemId", "tester");
Assert.That(id, Is.EqualTo(13));