MSTest 中是否有非静态 ClassInitialize 替代方案?
Is there a non-static ClassInitialize alternative in MSTest?
我正在使用 Visual Studio 2012 测试框架(我想那是 MSTest)。在各种测试方法 运行 之前,我有一些代码需要 运行 并且只需要一次 。 ClassInitialize 看起来很完美,直到我了解到它必须是静态的。
首先,我有一个 ChromeDriver:
的实例变量
private ChromeDriver driver;
我想我需要这样的东西,但不是静态的:
[ClassInitialize]
public static void Initialize() {
ChromeOptions options = new ChromeOptions();
options.AddArgument("test-type");
options.AddArgument("start-maximized");
options.LeaveBrowserRunning = true;
driver = new ChromeDriver(@"C:\MyStuff", options);
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(4));
}
上面的代码无法编译,因为 class 个实例正在这个静态方法中更新。但我不得不将其设置为静态,或者 运行 时间错误导致 "this has to be a static method" 和 "wrong signature" 等
如果我在 class 构造函数或 TestInitialize 方法中启动我的 Chrome 驱动程序(声明为 class 变量),一切正常,但是 一个新的浏览器 window 为每个测试打开 。因此,当我有 50 个测试方法时,我将打开 50 个 Chrome 实例,这很糟糕。
我只想为每个测试重用我的 driver
实例,而不必每次都启动一个新实例,这会打开一个新浏览器。
如何做到这一点?
我不确定它是否真的是 最佳 方法,但您可以将其设为静态变量 - 可以在之后从实例方法(您的测试)访问静态变量全部.
请注意,如果您尝试 运行 并行测试,这可能会导致问题。可能值得研究测试实例生命周期是什么 - 您可以在其中使用一个 实例 进行多个测试,在这种情况下,在构造函数中初始化实例变量可能是另一种合理的方法。
(除非初始化驱动程序真的需要很长时间,否则我很想在每次测试的基础上创建它...)
假设您的测试 class 包含 10 个测试。为了一个正确的测试,你的测试不应该受到其他测试的影响,你的测试也不应该影响其他人。
为了加强这一点,单元测试的开发人员定义了每个测试 运行 在它自己的测试 class 对象中。这使您可以自由更改测试 class 对象中的变量,而不会影响其他测试。
您希望您的初始化程序仅 运行 一次,并且所有测试都使用相同的变量。每个测试 运行s 在它自己的测试对象中,因此这些变量必须是共同的。最简单的方法是静态变量。
但是,如果您不想使用静态变量,请考虑使用 the singleton design pattern。唯一的单例对象包含您要在任何测试开始之前初始化的变量。
private class MySingleton
{
public static MySingleTon GetInstance()
{
if (theOneAndOnlyInstance == null)
{
theOneAndOnlyInstance = new MySingleton(...);
}
return theOneAndOnlyInstance;
}
private static MySingleton theOneAndOnlyInstance = null;
private MySingleton(...)
{
... // initialize the non-static test variables
}
#region test variables
public int TestVariableX {get; set;}
...
#endregion test variables
}
[TestMethod}
public void MyTest()
{
var myTestVariables = MySingleton.GetInstance();
...
}
优点是测试变量在任何人使用之前都不会创建或初始化。构造函数是私有的,因此唯一可以创建对象的是静态函数 GetInstance()。
我不确定这是否是最佳方法,但似乎可行。
您在这里唯一需要的是唯一的测试方法名称部分。
private const string TEST_METHOD_UNIQUE_NAME_PART = "test";
private static TestContext _testContext;
private static bool _isPreconditionCompleted;
private static int _numberOfCompletedTests;
private int NumberOfTestMethodsInClass
{
get
{
var className = _testContext.FullyQualifiedTestClassName.Split('.').Last().Trim();
var currentType = Assembly.GetExecutingAssembly().GetExportedTypes()
.Where(r => r.Name.Trim() == className).First();
return currentType.GetMethods().Where(r => r.Name.ToLower()
.Contains(TEST_METHOD_UNIQUE_NAME_PART)).Count();
}
}
[ClassInitialize]
public static void ClassSetUp(TestContext testContext)
{
_testContext = testContext;
_isPreconditionCompleted = false;
_numberOfCompletedTests = 0;
}
[TestInitialize]
public void SetUp()
{
if (!_isPreconditionCompleted)
{
PreConditionMethod();
}
}
[TestCleanup]
public void TearDown()
{
_numberOfCompletedTests++;
if (_numberOfCompletedTests == NumberOfTestMethodsInClass)
{
PostConditionMethod();
}
}
private void PreConditionMethod()
{
// Your code
_isPreconditionCompleted = true;
}
private void PostConditionMethod()
{
// Your code
}
我正在使用 Visual Studio 2012 测试框架(我想那是 MSTest)。在各种测试方法 运行 之前,我有一些代码需要 运行 并且只需要一次 。 ClassInitialize 看起来很完美,直到我了解到它必须是静态的。
首先,我有一个 ChromeDriver:
的实例变量private ChromeDriver driver;
我想我需要这样的东西,但不是静态的:
[ClassInitialize]
public static void Initialize() {
ChromeOptions options = new ChromeOptions();
options.AddArgument("test-type");
options.AddArgument("start-maximized");
options.LeaveBrowserRunning = true;
driver = new ChromeDriver(@"C:\MyStuff", options);
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(4));
}
上面的代码无法编译,因为 class 个实例正在这个静态方法中更新。但我不得不将其设置为静态,或者 运行 时间错误导致 "this has to be a static method" 和 "wrong signature" 等
如果我在 class 构造函数或 TestInitialize 方法中启动我的 Chrome 驱动程序(声明为 class 变量),一切正常,但是 一个新的浏览器 window 为每个测试打开 。因此,当我有 50 个测试方法时,我将打开 50 个 Chrome 实例,这很糟糕。
我只想为每个测试重用我的 driver
实例,而不必每次都启动一个新实例,这会打开一个新浏览器。
如何做到这一点?
我不确定它是否真的是 最佳 方法,但您可以将其设为静态变量 - 可以在之后从实例方法(您的测试)访问静态变量全部.
请注意,如果您尝试 运行 并行测试,这可能会导致问题。可能值得研究测试实例生命周期是什么 - 您可以在其中使用一个 实例 进行多个测试,在这种情况下,在构造函数中初始化实例变量可能是另一种合理的方法。
(除非初始化驱动程序真的需要很长时间,否则我很想在每次测试的基础上创建它...)
假设您的测试 class 包含 10 个测试。为了一个正确的测试,你的测试不应该受到其他测试的影响,你的测试也不应该影响其他人。
为了加强这一点,单元测试的开发人员定义了每个测试 运行 在它自己的测试 class 对象中。这使您可以自由更改测试 class 对象中的变量,而不会影响其他测试。
您希望您的初始化程序仅 运行 一次,并且所有测试都使用相同的变量。每个测试 运行s 在它自己的测试对象中,因此这些变量必须是共同的。最简单的方法是静态变量。
但是,如果您不想使用静态变量,请考虑使用 the singleton design pattern。唯一的单例对象包含您要在任何测试开始之前初始化的变量。
private class MySingleton
{
public static MySingleTon GetInstance()
{
if (theOneAndOnlyInstance == null)
{
theOneAndOnlyInstance = new MySingleton(...);
}
return theOneAndOnlyInstance;
}
private static MySingleton theOneAndOnlyInstance = null;
private MySingleton(...)
{
... // initialize the non-static test variables
}
#region test variables
public int TestVariableX {get; set;}
...
#endregion test variables
}
[TestMethod}
public void MyTest()
{
var myTestVariables = MySingleton.GetInstance();
...
}
优点是测试变量在任何人使用之前都不会创建或初始化。构造函数是私有的,因此唯一可以创建对象的是静态函数 GetInstance()。
我不确定这是否是最佳方法,但似乎可行。 您在这里唯一需要的是唯一的测试方法名称部分。
private const string TEST_METHOD_UNIQUE_NAME_PART = "test";
private static TestContext _testContext;
private static bool _isPreconditionCompleted;
private static int _numberOfCompletedTests;
private int NumberOfTestMethodsInClass
{
get
{
var className = _testContext.FullyQualifiedTestClassName.Split('.').Last().Trim();
var currentType = Assembly.GetExecutingAssembly().GetExportedTypes()
.Where(r => r.Name.Trim() == className).First();
return currentType.GetMethods().Where(r => r.Name.ToLower()
.Contains(TEST_METHOD_UNIQUE_NAME_PART)).Count();
}
}
[ClassInitialize]
public static void ClassSetUp(TestContext testContext)
{
_testContext = testContext;
_isPreconditionCompleted = false;
_numberOfCompletedTests = 0;
}
[TestInitialize]
public void SetUp()
{
if (!_isPreconditionCompleted)
{
PreConditionMethod();
}
}
[TestCleanup]
public void TearDown()
{
_numberOfCompletedTests++;
if (_numberOfCompletedTests == NumberOfTestMethodsInClass)
{
PostConditionMethod();
}
}
private void PreConditionMethod()
{
// Your code
_isPreconditionCompleted = true;
}
private void PostConditionMethod()
{
// Your code
}