.Net 核心库:如何使用 xUnit 测试私有方法
.Net core library: How to test private methods using xUnit
最新的 xunit 框架不允许在使用 .Net Core 框架编译时在库代码中测试 运行ners(这对于正常的 Visual Studio 代码是允许的)。解决方案是为 运行 您的测试用例创建一个单独的测试项目。
我遇到的问题是我要测试的某些方法是 'private' 方法。 如何在不指定范围的情况下从我的测试项目中调用这些方法 'public'?
更一般的问题是:当使用私有方法的public方法不能使用时,如何测试私有方法?(因为它的交互式性质 - 也就是说,它有一个 ReadLine(); 包含在其中)
可能的解决方案:
1) 制作私有方法 public 并以表明它们不应在外部使用的方式重命名它们。 (使用 'private' 或 'internal' 作为名称的一部分)
2) 创建一个可以设置为 true 的“public static bool Testflag”字段,以绕过 public 方法的交互部分,确保对其所有部分进行测试。
(虽然我已经使用了上述两种方法 - 我真的不喜欢它,因为私有方法应该保持私有并且标志增加了很多额外的复杂性。有人遇到过同样的问题吗?你是如何解决的?
一个快速的解决方案是让 private
个您想要测试的成员 internal
。
然后您可以将 InternalsVisibleTo
属性添加到主图书馆的 AssemblyInfo.cs。这将允许您的测试库(没有其他库,没有反射)访问主库中的内部 methods/classes。
例如
[assembly: InternalsVisibleTo("Library.Tests")]
为了测试内部 类, 是正确的。
据推测,私有方法是私有的是有充分理由的。他们不应该成为内部人士,这也是有充分理由的。
(有些人认为我们不应该为私有方法编写单元测试。为什么?好吧,因为它们是 "private"!但是如果你真的要为私有方法编写单元测试:)
这是测试私有方法的更好方法:
public class Hello
{
private string _firstName { get; set; }
private string _lastName { get; set; }
public Hello(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string HelloMan()
{
if (string.IsNullOrEmpty(_firstName))
throw new Exception("Missing First Name.");
return this.HelloMan(_firstName, _lastName);
}
private string HelloMan(string firstName, string lastName)
{
return $"Hello {firstName} {lastName}!";
}
}
测试过程如下:
public class HelloTests
{
[Fact]
public void PrivateHelloManShouldBeWellFormated()
{
// Arrange
var firstName = "John";
var lastName = "Doe";
Type type = typeof(Hello);
var hello = Activator.CreateInstance(type, firstName, lastName);
MethodInfo method = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => x.Name == "HelloMan" && x.IsPrivate)
.First();
//Act
var helloMan = (string)method.Invoke(hello, new object [] {firstName, lastName});
//Assert
helloMan
.Should()
.StartWith("Hello")
.And
.EndWith("!")
.And
.Contain("John")
.And
.Contain("Doe");
}
}
参考:
如果您需要测试私有方法,那么您就是代码异味。您应该检查您的体系结构,而没有理由测试私有属性或方法。如果你需要这个 - 它不是私有方法,它是另一个 class.
的 public 方法
特别针对仇恨者阅读单元测试艺术第 2 版中的第 8.2.1 段。只有 public 方法(契约)应该是测试,仅此而已。
老式的逐步优化使得测试私有方法很有用,即使不是纯粹主义者,也适用于实用主义者。
将@Tohid 的出色回答更进一步,这个class 可用于调用静态或实例私有方法
public static class TestPrivate
{
public static T StaticMethod<T>(Type classType, string methodName, object[] callParams)
{
var methodList = classType
.GetMethods(BindingFlags.NonPublic | BindingFlags.Static);
if (methodList is null || !methodList.Any())
throw new EntryPointNotFoundException();
var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);
var output = (T)method.Invoke(null, callParams);
return output;
}
public static T InstanceMethod<T>(object instance, string methodName, object[] callParams)
{
var classType = instance.GetType();
var methodList = classType
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
if (methodList is null || !methodList.Any())
throw new EntryPointNotFoundException();
var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);
var output = (T)method.Invoke(instance, callParams);
return output;
}
}
然后这样使用:
{
var result = TestPrivate.InstanceMethod<bool>(InstanceOfClass, "privateMethod", new object[] { param1, param2 });
Assert.True(result);
var actual = TestPrivate.StaticMethod<myReturnType>(typeof(StaticClass), "privateMethod", new object[] { param1, param2, param3 });
Assert.True(actual.prop == expected.prop);
}
最新的 xunit 框架不允许在使用 .Net Core 框架编译时在库代码中测试 运行ners(这对于正常的 Visual Studio 代码是允许的)。解决方案是为 运行 您的测试用例创建一个单独的测试项目。
我遇到的问题是我要测试的某些方法是 'private' 方法。 如何在不指定范围的情况下从我的测试项目中调用这些方法 'public'?
更一般的问题是:当使用私有方法的public方法不能使用时,如何测试私有方法?(因为它的交互式性质 - 也就是说,它有一个 ReadLine(); 包含在其中)
可能的解决方案:
1) 制作私有方法 public 并以表明它们不应在外部使用的方式重命名它们。 (使用 'private' 或 'internal' 作为名称的一部分)
2) 创建一个可以设置为 true 的“public static bool Testflag”字段,以绕过 public 方法的交互部分,确保对其所有部分进行测试。
(虽然我已经使用了上述两种方法 - 我真的不喜欢它,因为私有方法应该保持私有并且标志增加了很多额外的复杂性。有人遇到过同样的问题吗?你是如何解决的?
一个快速的解决方案是让 private
个您想要测试的成员 internal
。
然后您可以将 InternalsVisibleTo
属性添加到主图书馆的 AssemblyInfo.cs。这将允许您的测试库(没有其他库,没有反射)访问主库中的内部 methods/classes。
例如
[assembly: InternalsVisibleTo("Library.Tests")]
为了测试内部 类,
据推测,私有方法是私有的是有充分理由的。他们不应该成为内部人士,这也是有充分理由的。
(有些人认为我们不应该为私有方法编写单元测试。为什么?好吧,因为它们是 "private"!但是如果你真的要为私有方法编写单元测试:)
这是测试私有方法的更好方法:
public class Hello
{
private string _firstName { get; set; }
private string _lastName { get; set; }
public Hello(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string HelloMan()
{
if (string.IsNullOrEmpty(_firstName))
throw new Exception("Missing First Name.");
return this.HelloMan(_firstName, _lastName);
}
private string HelloMan(string firstName, string lastName)
{
return $"Hello {firstName} {lastName}!";
}
}
测试过程如下:
public class HelloTests
{
[Fact]
public void PrivateHelloManShouldBeWellFormated()
{
// Arrange
var firstName = "John";
var lastName = "Doe";
Type type = typeof(Hello);
var hello = Activator.CreateInstance(type, firstName, lastName);
MethodInfo method = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => x.Name == "HelloMan" && x.IsPrivate)
.First();
//Act
var helloMan = (string)method.Invoke(hello, new object [] {firstName, lastName});
//Assert
helloMan
.Should()
.StartWith("Hello")
.And
.EndWith("!")
.And
.Contain("John")
.And
.Contain("Doe");
}
}
参考:
如果您需要测试私有方法,那么您就是代码异味。您应该检查您的体系结构,而没有理由测试私有属性或方法。如果你需要这个 - 它不是私有方法,它是另一个 class.
的 public 方法特别针对仇恨者阅读单元测试艺术第 2 版中的第 8.2.1 段。只有 public 方法(契约)应该是测试,仅此而已。
老式的逐步优化使得测试私有方法很有用,即使不是纯粹主义者,也适用于实用主义者。
将@Tohid 的出色回答更进一步,这个class 可用于调用静态或实例私有方法
public static class TestPrivate
{
public static T StaticMethod<T>(Type classType, string methodName, object[] callParams)
{
var methodList = classType
.GetMethods(BindingFlags.NonPublic | BindingFlags.Static);
if (methodList is null || !methodList.Any())
throw new EntryPointNotFoundException();
var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);
var output = (T)method.Invoke(null, callParams);
return output;
}
public static T InstanceMethod<T>(object instance, string methodName, object[] callParams)
{
var classType = instance.GetType();
var methodList = classType
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
if (methodList is null || !methodList.Any())
throw new EntryPointNotFoundException();
var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);
var output = (T)method.Invoke(instance, callParams);
return output;
}
}
然后这样使用:
{
var result = TestPrivate.InstanceMethod<bool>(InstanceOfClass, "privateMethod", new object[] { param1, param2 });
Assert.True(result);
var actual = TestPrivate.StaticMethod<myReturnType>(typeof(StaticClass), "privateMethod", new object[] { param1, param2, param3 });
Assert.True(actual.prop == expected.prop);
}