我正在尝试在Visual Studio 2010中的当前项目上开始使用单元测试。但是,我的类结构包含许多接口和抽象类继承关系。
如果两个类派生自同一个抽象类或接口,我希望能够在它们之间共享测试代码。我不确定该怎么做。我想我为每个要测试的接口创建了一个测试类,但我不确定将具体类提供给适用的单元测试的正确方法。
更新
好的,这是一个例子。假设我有一个接口IEmployee,它由一个抽象类Employee实现,然后由两个具体的类Worker和Employee继承。 (代码如下所示)
现在说我想创建适用于所有IEmployees或Employees的测试。或者为特定类型的员工创建特定测试。例如,我可能想断言将IEmployee.Number设置为小于零的数字,因为任何IEmployee实现都会引发异常。我更喜欢从任何IEmployee的角度编写测试,然后能够在IEmployee的任何实现上使用测试。
这是另一个例子。我可能还想断言,将任何员工的休假时间设置为小于零的值和错误。然而,我可能还希望有不同的测试适用于特定的Employee特定版本。假设我想测试一下,如果提供超过14天的假期,那么工作人员会抛出异常,但最多可以提供36位经理。
public interface IEmployee
{
string Name {get; set;}
int Number {get; set;}
}
public abstract class Employee:IEmploee
{
string Name {get; set;}
int Number {get;set;}
public abstract int VacationTime(get; set;)
}
public abstract class Worker:IEmployee
{
private int v;
private int vTime;
public abstract int VacationTime
{
get
{
return VTime;
}
set
{
if(value>36) throw new ArgumentException("Exceeded allowed vaction");
if(value<0)throw new ArgumentException("Vacation time must be >0");
vTime= value;
}
}
public void DoSomWork()
{
//Work
}
}
public abstract class Manager:IEmployee
{
public abstract int VacationTime
{
get
{
return VTime;
}
set
{
if(value>14) throw new ArgumentException("Exceeded allowed vaction");
if(value<0)throw new ArgumentException("Vacation time must be >0");
vTime= value;
}
}
public void DoSomeManaging()
{
//manage
}
}
所以我想我正在寻找的是一个允许我嵌套单元测试的工作流程。因此,例如,当我测试Manager类时,我想首先测试它是否通过了Employee和IEmployee测试,然后测试特定成员,例如DoSomeManaging()
。
答案 0 :(得分:6)
我想我知道你的意思。我有同样的问题。
我的解决方案是创建一个用于测试的层次结构。我将使用您展示的相同示例。
首先,为基本IEmployee提供一个抽象测试类。
它有两个主要内容: 一世。您想要的所有测试方法。 II。一种抽象方法,它返回所需的IEmployee实例。
[TestClass()]
public abstract class IEmployeeTests
{
protected abstract GetIEmployeeInstance();
[TestMethod()]
public void TestMethod1()
{
IEmployee target = GetIEmployeeInstance();
// do your IEmployee test here
}
}
其次,您为IEmployee的每个实现都有一个测试类,实现抽象方法并提供适当的IEmployee实例。
[TestClass()]
public class WorkerTests : IEmployeeTests
{
protected override GetIEmployeeInstance()
{
return new Worker();
}
}
[TestClass()]
public class ManagerTests : IEmployeeTests
{
protected override GetIEmployeeInstance()
{
return new Manager();
}
}
您可以看到一切按预期工作,VS为TestView窗口中的每个WorkerTests和ManagerTests类提供了预期的测试方法。 您可以运行它们并获得IEmployee接口的每个实现的测试结果,只需在基本的IEmployeeTests类中创建测试。
您始终可以为派生的WorkerTests和ManagerTests类添加特定测试。
现在的问题是,实现多个接口的类怎么样,让我们说EmployedProgrammer?
public EmployedProgrammer : IEmployee, IProgrammer
{
}
我们在C#中没有多重继承,所以这不是一个选项:
[TestClass()]
public EmployedProgrammerIEmployeeTests : IEmployeeTests, IProgrammerTests
{
// this doesn't compile as IEmployeeTests, IProgrammerTests are classes, not interfaces
}
对于这种情况,解决方案是拥有以下测试类:
[TestClass()]
public EmployedProgrammerIEmployeeTests : IEmployeeTests
{
protected override GetIEmployeeInstance()
{
return new EmployedProgrammer();
}
}
[TestClass()]
public EmployedProgrammerIProgrammerTests : IProgrammerTests
{
protected override GetIProgrammerInstance()
{
return new EmployedProgrammer();
}
}
与
[TestClass()]
public abstract class IProgrammerTests
{
protected abstract GetIProgrammerInstance();
[TestMethod()]
public void TestMethod1()
{
IProgrammer target = GetIProgrammerInstance();
// do your IProgrammerTest test here
}
}
我正在使用它,效果很好。 希望它有所帮助。
此致 何
答案 1 :(得分:0)
我认为你想要做的是为抽象类中的方法创建单元测试。
我不确定想要在抽象类上测试受保护的方法是有意义的,但是如果你坚持只是在专门用于单元测试的类中扩展类。这样,您可以通过扩展类上的公共方法公开要测试的抽象类上的受保护方法,该扩展类只调用抽象类上的方法。
如果您希望在抽象类中使用单元测试的方法,我建议将它们重构为单独的类,并将它们公开为公共方法并将其置于测试之下。尝试从“测试优先”的角度来看待你的继承树,我很确定你也会想出那个解决方案(或类似的解决方案)。
答案 2 :(得分:0)
您似乎已经描述了Visual Studio 2010单元测试不支持的“复合单元测试”。根据这个article,这些事情可以在MbUnit中完成。可以在Visual Studio 2010中创建abstract tests,这可能不是您想要的。 Here描述了如何在VS(继承示例部分)中实现抽象测试。
答案 3 :(得分:0)
使用microsoft moles进行更好的测试。所以你可以轻松地模拟抽象基类/静态方法等。有关详细信息,请参阅以下帖子
detouring-abstract-base-classes-using-moles
BenzCar benzCar = new BenzCar();
new MCar(benzCar)
{
Drive= () => "Testing"
}.InstanceBehavior = MoleBehaviors.Fallthrough;
var hello = child.Drive();
Assert.AreEqual("Benz Car driving. Testing", hello);
答案 4 :(得分:0)
对多个类运行相同测试的愿望通常意味着您有机会将要测试的行为提取到单个类中(无论是基类还是组成现有类的全新类)。
考虑一下您的示例:不是在Worker
和Manager
中实施休假限制,而是将新成员变量添加到Employee
,'MaximumVacationDays',在员工类'setter中实施限制,并检查那里的限制:
abstract class Employee {
private int maximumVacationDays;
protected Employee(int maximumVacationDays) {
this.maximumVacationDays = maximumVacationDays
}
public int VacationDays {
set {
if (value > maximumVacationDays)
throw new ArgumentException("Exceeded maximum vacation");
}
}
}
class Worker: Employee {
public Worker(): Employee(14) {}
}
class Manager: Employee {
public Manager(): Employee(36) {}
}
现在你只有一种方法可以测试,而且需要维护更少的代码。