如何在 NUnit 中测试同一类型的多个对象
How to test multiple objects of the same type in NUnit
我目前是第一次尝试学习 NUnit。
对于我想使用 TDD 开发的 C# 程序,我决定先编写 User
class。这个 User
class 将(语义上)看起来像这样:
using System;
namespace SSH_file_drop
{
public class User
{
private Boolean authenticated = false;
public string userName = null;
//one-time object 'transmission' instance to allow single-use file transmission field
Transmission fileVehicle = null;
//property to discern whether user has been correctly authenticated
public Boolean isAuthenticated
{
get;
}
public Boolean canSend ()
{
if (authenticated)
{
return this.userType != "sender";
}
return false;
}
public User(String inputUName)
{
String userName = inputUName;
}
public static void generateUser(string userName)
{
//contact server to attempt to register new user
}
public void ChangePassword(String oldPassword, String newPassword)
{
//ask server to change user password
}
public Boolean SetUpTransmission()
{
if (canSend())
{
try
{
fileVehicle = new Transmission(this);
return true;
}
catch (e)
{
//write exception message
return false;
}
}
else
{
return false;
}
}
}
}
目前真的只是占位符代码。
为了测试 User
class 但是我正在尝试编写一个 TestFixture 来创建用户 class 的单独实例,并以某种方式持久存储它们直到拆卸,执行对每个对象进行相同的测试。
我的想法是创建一个 User
对象数组作为 TestCase 数据源,以通过 [Order(<n>)]
属性(栏用户实例初始化测试方法)按顺序测试所有内容,但我已经阅读here 将在运行时为其中的每个方法创建 TestFixture 的新实例,因此我无法以这种方式修改测试夹具中的持久数据。
因为我正在尝试实现有状态逻辑 - User
对象 isAuthenticated() (并且此身份验证依赖于此后的后续测试,因为所有用户数据都被建模为存储在远程数据库中),是有没有一种方法可以在不使用重复操作(创建对象、验证、检查用户类型等)并因此创建多个断言的情况下创建测试?
我们在这种情况下采用的方法是创建辅助方法,以制造具有适当状态的被测 class 实例。
这实际上就是您提到的方法,只是方法略有不同。
你提出的方法要考虑的其他问题,我建议的方法会避免,是有序的 and/or 并行执行测试:如果你有一系列有状态的对象,测试将无法可靠地 运行 并行或乱序。
请注意,为了完全支持这种测试开发模式,您可能需要引入仅测试方法 and.or 构造函数。
例如,您 class 中的 IsAuthenticated 是只读的 属性。为了模拟此 属性 打开或关闭,您可能需要引入一个仅用于测试的构造函数(我倾向于避免这样做,因为有人会在某些时候使用它,即使您已经记录它仅用于测试) 或更改 属性 的实现并添加 setter 方法。
如果您将 Authenticated 属性 更改为使用后备存储成员,那么您可以添加一个仅测试方法(尽管其他开发人员有可能使用此方法,如果它很好- named,使用起来比构造函数更明显)。
private bool m_IsAuthenticated;
public bool IsAuthenticated {
get {
return m_IsAuthenticated;
}
}
public void Set_IsAuthenticated_ForTestONLY(bool value) {
m_IsAuthenticated = value;
}
正在回答您问题中有关 NUnit 如何工作的部分...
你引用的关于 NUnit 中夹具实例的生命周期的答案具有误导性,并且有点令人惊讶,因为它没有什么大的奥秘!
NUnit V2 的主要开发者 Jim Newkirk 的引述只是表达了他多么希望自己完成了 NUnit V2。他在 xUnit.net 框架中实现了这些想法,但它与 NUnit 3 无关。
NUnit 3 与 NUnit V2 一样,创建一个 TestFixture
实例,用于所有测试。您可以使用 [OneTimeSetUp]
创建要在测试中使用的对象并将它们存储为 class.If 的成员这些对象是有状态的,您应该避免对使用的夹具中的测试使用并行测试执行他们。
如果每个测试都需要额外的设置,那么您可以使用 [SetUp]
来达到这个目的,并使用 [TearDown]
删除任何会对后续测试产生负面影响的更改。
您还可以指定测试的顺序,但这通常不是一个好主意,因为一个失败的测试可能会导致后续测试也失败。如果可以,尽量让每个测试独立。
另请注意,如果您希望能够针对不同类型的对象多次 运行 相同的夹具,参数化夹具是一个不错的选择。只需将足够的信息传递给构造函数,以便在一次性设置中完成正确的对象初始化。
请务必阅读文档中上述所有内容的详细信息:https://github.com/nunit/docs/wiki
我目前是第一次尝试学习 NUnit。
对于我想使用 TDD 开发的 C# 程序,我决定先编写 User
class。这个 User
class 将(语义上)看起来像这样:
using System;
namespace SSH_file_drop
{
public class User
{
private Boolean authenticated = false;
public string userName = null;
//one-time object 'transmission' instance to allow single-use file transmission field
Transmission fileVehicle = null;
//property to discern whether user has been correctly authenticated
public Boolean isAuthenticated
{
get;
}
public Boolean canSend ()
{
if (authenticated)
{
return this.userType != "sender";
}
return false;
}
public User(String inputUName)
{
String userName = inputUName;
}
public static void generateUser(string userName)
{
//contact server to attempt to register new user
}
public void ChangePassword(String oldPassword, String newPassword)
{
//ask server to change user password
}
public Boolean SetUpTransmission()
{
if (canSend())
{
try
{
fileVehicle = new Transmission(this);
return true;
}
catch (e)
{
//write exception message
return false;
}
}
else
{
return false;
}
}
}
}
目前真的只是占位符代码。
为了测试 User
class 但是我正在尝试编写一个 TestFixture 来创建用户 class 的单独实例,并以某种方式持久存储它们直到拆卸,执行对每个对象进行相同的测试。
我的想法是创建一个 User
对象数组作为 TestCase 数据源,以通过 [Order(<n>)]
属性(栏用户实例初始化测试方法)按顺序测试所有内容,但我已经阅读here 将在运行时为其中的每个方法创建 TestFixture 的新实例,因此我无法以这种方式修改测试夹具中的持久数据。
因为我正在尝试实现有状态逻辑 - User
对象 isAuthenticated() (并且此身份验证依赖于此后的后续测试,因为所有用户数据都被建模为存储在远程数据库中),是有没有一种方法可以在不使用重复操作(创建对象、验证、检查用户类型等)并因此创建多个断言的情况下创建测试?
我们在这种情况下采用的方法是创建辅助方法,以制造具有适当状态的被测 class 实例。
这实际上就是您提到的方法,只是方法略有不同。
你提出的方法要考虑的其他问题,我建议的方法会避免,是有序的 and/or 并行执行测试:如果你有一系列有状态的对象,测试将无法可靠地 运行 并行或乱序。
请注意,为了完全支持这种测试开发模式,您可能需要引入仅测试方法 and.or 构造函数。
例如,您 class 中的 IsAuthenticated 是只读的 属性。为了模拟此 属性 打开或关闭,您可能需要引入一个仅用于测试的构造函数(我倾向于避免这样做,因为有人会在某些时候使用它,即使您已经记录它仅用于测试) 或更改 属性 的实现并添加 setter 方法。
如果您将 Authenticated 属性 更改为使用后备存储成员,那么您可以添加一个仅测试方法(尽管其他开发人员有可能使用此方法,如果它很好- named,使用起来比构造函数更明显)。
private bool m_IsAuthenticated;
public bool IsAuthenticated {
get {
return m_IsAuthenticated;
}
}
public void Set_IsAuthenticated_ForTestONLY(bool value) {
m_IsAuthenticated = value;
}
正在回答您问题中有关 NUnit 如何工作的部分...
你引用的关于 NUnit 中夹具实例的生命周期的答案具有误导性,并且有点令人惊讶,因为它没有什么大的奥秘!
NUnit V2 的主要开发者 Jim Newkirk 的引述只是表达了他多么希望自己完成了 NUnit V2。他在 xUnit.net 框架中实现了这些想法,但它与 NUnit 3 无关。
NUnit 3 与 NUnit V2 一样,创建一个 TestFixture
实例,用于所有测试。您可以使用 [OneTimeSetUp]
创建要在测试中使用的对象并将它们存储为 class.If 的成员这些对象是有状态的,您应该避免对使用的夹具中的测试使用并行测试执行他们。
如果每个测试都需要额外的设置,那么您可以使用 [SetUp]
来达到这个目的,并使用 [TearDown]
删除任何会对后续测试产生负面影响的更改。
您还可以指定测试的顺序,但这通常不是一个好主意,因为一个失败的测试可能会导致后续测试也失败。如果可以,尽量让每个测试独立。
另请注意,如果您希望能够针对不同类型的对象多次 运行 相同的夹具,参数化夹具是一个不错的选择。只需将足够的信息传递给构造函数,以便在一次性设置中完成正确的对象初始化。
请务必阅读文档中上述所有内容的详细信息:https://github.com/nunit/docs/wiki