如何访问静态 Class 私有字段以在 C# 中使用 Microsoft Fakes 对其方法进行单元测试
How to access a Static Class Private fields to unit test its methods using Microsoft Fakes in C#
我有下面的静态 class 和其中的一个方法,我需要对其进行单元测试。我可以 但是这个方法有一个 if 条件,它使用一个布尔私有变量,如果它的值为 false,那么它会执行该 if 条件中的步骤。
public static class Logger
{
private static bool bNoError = true;
public static void Log()
{
if (!bNoError)
{
//Then execute the logic here
}
else
{
//else condition logic here
}
}
}
有没有一种方法可以将私有字段 bNoError 值设置为 true,这样我就可以使用一种测试方法来测试 if 条件下的逻辑。
嗯,一个方法就是在原来的class上加一个方法然后shim一下:
public static class Logger
{
// .... other stuff here
private static void SetbNoError(bool flag)
{
// leave implementation empty
}
}
然后在你的测试中:
ShimLogger.SetbNoErrorBool = flag => bNoError = flag;
您可以使用反射来进行测试。虽然这很违法。
using System.Reflection;
................
................
var field = typeof(Logger).GetField("bNoError",
BindingFlags.Static |
BindingFlags.NonPublic);
// Normally the first argument to "SetValue" is the instance
// of the type but since we are mutating a static field we pass "null"
field.SetValue(null, false);
出于单元测试的目的,Microsoft 已经实现了一些帮助程序 类 (PrivateType and PrivateObject),它们在此类场景中使用反射。
PrivateType myTypeAccessor = new PrivateType(typeof(TypeToAccess));
myTypeAccessor.SetStaticFieldOrProperty("bNoError", false);
PrivateType 用于静态访问,而 PrivateObject 用于针对实例化对象进行测试。
您需要包含 Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll 中的 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间才能使用它们。
我不建议在单元测试中使用反射。在有很多开发人员的大型项目上很难维护,因为它不容易重构,发现。
此外,单元测试应该测试 class 的某些行为。如果在被测试的 class 中存在某种错误处理,它应该不仅可以被单元测试访问,而且在真实场景中也可以被开发人员(调用者)访问,而不是被封装隐藏。如果我在实际场景中调用某个方法,我希望我可以获得错误状态或捕获异常。 通常我更喜欢异常。 异常错误处理可以通过单元测试轻松测试,不需要反射来破坏封装。
所以我的建议是public:
public static class Logger
{
private bool bNoError = true;
public static void Log()
{
if (!bNoError)
{
//Then execute the logic here
}
else
{
//else condition logic here
}
}
public static bool IsAnyError()
{
return !bNoError;
}
}
有时,您可能过于接近要测试的代码,以至于忘记了考虑全局并开始过多地关注实现。你开始思考 "When this variable is set, ...",而不是思考 "When XXX happens, ..."。当您达到这一点时,这表明您可能过于关注实施,并且您 运行 面临创建非常脆弱的测试的重大风险,如果您对实施进行任何更改,这些测试将会中断。
@Martin Noreke 的 post 介绍了如何做您正在尝试做的事情。不过对我来说,感觉就像你在测试错误的东西。看来您要编写的下一个测试是 "Do XXX with Logger and test that bNoError
is set to false
"
显然它有点依赖于 Logger 的其余部分 class,但感觉也许另一种方法可能是:
Call Logger method XXX, shimming dependencies if necessary in order to trigger error state.
Call Logger.Log and validate expected behaviour
假设 bNoError
有办法重置回 true
,那么您可以:
Call Logger method YYY, shimming dependencies if necessary to trigger error cleanup
Call Logger.Log and validate expected behaviour
我有下面的静态 class 和其中的一个方法,我需要对其进行单元测试。我可以 但是这个方法有一个 if 条件,它使用一个布尔私有变量,如果它的值为 false,那么它会执行该 if 条件中的步骤。
public static class Logger
{
private static bool bNoError = true;
public static void Log()
{
if (!bNoError)
{
//Then execute the logic here
}
else
{
//else condition logic here
}
}
}
有没有一种方法可以将私有字段 bNoError 值设置为 true,这样我就可以使用一种测试方法来测试 if 条件下的逻辑。
嗯,一个方法就是在原来的class上加一个方法然后shim一下:
public static class Logger
{
// .... other stuff here
private static void SetbNoError(bool flag)
{
// leave implementation empty
}
}
然后在你的测试中:
ShimLogger.SetbNoErrorBool = flag => bNoError = flag;
您可以使用反射来进行测试。虽然这很违法。
using System.Reflection;
................
................
var field = typeof(Logger).GetField("bNoError",
BindingFlags.Static |
BindingFlags.NonPublic);
// Normally the first argument to "SetValue" is the instance
// of the type but since we are mutating a static field we pass "null"
field.SetValue(null, false);
出于单元测试的目的,Microsoft 已经实现了一些帮助程序 类 (PrivateType and PrivateObject),它们在此类场景中使用反射。
PrivateType myTypeAccessor = new PrivateType(typeof(TypeToAccess));
myTypeAccessor.SetStaticFieldOrProperty("bNoError", false);
PrivateType 用于静态访问,而 PrivateObject 用于针对实例化对象进行测试。
您需要包含 Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll 中的 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间才能使用它们。
我不建议在单元测试中使用反射。在有很多开发人员的大型项目上很难维护,因为它不容易重构,发现。
此外,单元测试应该测试 class 的某些行为。如果在被测试的 class 中存在某种错误处理,它应该不仅可以被单元测试访问,而且在真实场景中也可以被开发人员(调用者)访问,而不是被封装隐藏。如果我在实际场景中调用某个方法,我希望我可以获得错误状态或捕获异常。 通常我更喜欢异常。 异常错误处理可以通过单元测试轻松测试,不需要反射来破坏封装。
所以我的建议是public:
public static class Logger
{
private bool bNoError = true;
public static void Log()
{
if (!bNoError)
{
//Then execute the logic here
}
else
{
//else condition logic here
}
}
public static bool IsAnyError()
{
return !bNoError;
}
}
有时,您可能过于接近要测试的代码,以至于忘记了考虑全局并开始过多地关注实现。你开始思考 "When this variable is set, ...",而不是思考 "When XXX happens, ..."。当您达到这一点时,这表明您可能过于关注实施,并且您 运行 面临创建非常脆弱的测试的重大风险,如果您对实施进行任何更改,这些测试将会中断。
@Martin Noreke 的 post 介绍了如何做您正在尝试做的事情。不过对我来说,感觉就像你在测试错误的东西。看来您要编写的下一个测试是 "Do XXX with Logger and test that bNoError
is set to false
"
显然它有点依赖于 Logger 的其余部分 class,但感觉也许另一种方法可能是:
Call Logger method XXX, shimming dependencies if necessary in order to trigger error state.
Call Logger.Log and validate expected behaviour
假设 bNoError
有办法重置回 true
,那么您可以:
Call Logger method YYY, shimming dependencies if necessary to trigger error cleanup
Call Logger.Log and validate expected behaviour