假设我有一个类库的项目。我在该库中有一个类,这个类有一些只在类中使用的方法。像这样:
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范围,我无法做到这一点。处理这种情况的正确方法是什么?
答案 0 :(得分:4)
猜猜这取决于你对单位是什么的看法,对吧?我通常为可访问的接口编写单元测试并忽略私有东西。我曾与那些将私有物品保护(java)以进行单元测试访问的人合作。我真的不喜欢这种方法,因为它牺牲了类设计的清晰度以便进行测试访问。
答案 1 :(得分:3)
我只测试公共方法(并且我不关心覆盖率指标,我关心有效的功能)。
请注意,如果公共方法不使用内部方法,则内部方法不需要存在!
答案 2 :(得分:2)
答案 3 :(得分:2)
这是一篇关于该主题的好文章:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
我个人只是避免编写任何非常复杂的私有方法。还有其他方法可以封装您不想公开的行为,同时还能让您自己测试应该隐藏的内容。我认为完美的封装和可测试性之间存在权衡。完美的封装很难实现,通常更有利于让自己更深入地了解这些类。这可能是值得商榷的。
答案 4 :(得分:1)
很多人会说你不应该测试内部方法,而是通过公共API测试它们。无论如何,如果你真的想要访问这些私人成员,你可以使用反射。
答案 5 :(得分:1)
有两种情况:要么从某些公共方法调用您的私有方法,在这种情况下,您可以通过该方法测试它们。或者,它们不会从一些公共方法调用,它们根本不能被调用,是死代码,应该删除,而不是测试。
请注意,如果您正在进行TDD,私有方法只能通过从公共方法中提取它们来生成,在这种情况下,它们已经自动测试。
答案 6 :(得分:0)
Visual Studio可以为您生成私有访问器。在MSDN上查看Unit Tests for Private, Internal, and Friend Methods。我相信VS2005只是为您的单元测试项目生成并添加了私有访问器类。因此,当事情发生变化时,你必须重新生成它们。但是,VS2008会生成一个私有访问器程序集,并使其可用于单元测试项目。你正在使用NUnit,但我认为它应该没问题。看看这个。这样,您可以保持实际代码不受任何测试相关代码和/或黑客攻击。
答案 7 :(得分:0)
过去,我在与被测试的类相同的命名空间和程序集中创建了测试夹具,以测试内部方法。我不是说内部方法是否应该进行测试。实际上,你可以测试它们然后重构。
我还创建了部分类来测试私有方法,并在整个部分(在自己的文件中)使用了编译器指令。再说一次,并不是说这是最好的,但有时你需要继续前进。
在构建时,我们可以在调试或发布模式下运行单元测试,如果需要,我们可以从任一构建中剥离测试代码,因此在测试代码中没有 harm 被测代码;如果有的话,它在代码和数据 - 一起= object或object-and-doc-comments = documentation-object的论证中是相似的。换句话说:代码和数据以及测试和文档注释一起=内聚单元。
构建期间的额外时间可以忽略不计。
答案 8 :(得分:0)
我使用了几种方法来做到这一点。我已经使我的私有方法受到保护,这样我可以在单元测试中继承该类,并创建一个“帮助器”类来测试这些方法。另一个是反思。 IMO的反思更容易,但是我们的课程应该如何设计用于测试。这是我正在谈论的简化版本。
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;
}
}
给出一个像这样的课程
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
internal string GetPrettyName()
{
return string.Concat(FirstName, " ", LastName);
}
static internal int GetSystemId(string userName)
{
// some magic here
return 13;
}
}
你可以这样使用
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));
答案 9 :(得分:-2)
我喜欢把我的单元测试放在他们测试的同一个类中。这有两个好处,第一个是它解决了你遇到的问题,第二个是你永远不会丢失或忘记它们,因为如果它们在一个单独的组件中,往往最终会出现这种情况。
不是每个人都同意这种方法(参见我之前提到的SO question)但是我还没有找到或有任何瑕疵向我指出。我一直在进行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
}