超级简单:一个超轻量级的测试平台

    有时候,在开发环境中测试核心业务职能,可能需要花很长而宝贵的时间。

    幸运地是有很多简单而且自动化的测试平台。尽管如此,这个操作却不会总是能提供预期的结果。有时,它需要人为进行检查和评价。

    此外,在一个大型项目中,编译几十个类,每一点变动是非常耗费时间和精力的,自动测试将不会达到预期的行为。OBP控制台测试框架是一个非常简单,轻量级的类库,它用于测试你的业务对象,但它没有一个像windows forms,web, silverlight 、WPF的图形界面。

    OBP控制台测试库的主要目的是:在将代码整合到相关项目中之前,检查它的核心业务功能。

  在测试类有下列行为:

    在运行时,通过自定义属性和反射找出测试类和测试方法

    提供测试人员提供一个简单菜单式的控制台的用户界面

    允许重复使用菜单(控制台上的)

    便利地显示的业务对象的集合,并能便利从中选择单独的业务对象进行测试。

  文章中的一些定义

    测试方法 :能通过执行一些动作来验证开发项目的行为、动作、过程。

    测试类 :包含一个或多个测试方法的类。它也可能还包含测试类成员来执行重叠测试。

    菜单 :是在控制台上显示一系列的活动,允许测试人员使用键盘调用一个给定的测试方法。

  结构:

   整个类库是建立在一个简单的类上:CustomConsoleTest。这个类完成以下行动:

   1、它使用反射浏览所有子类的方法并找出有“TestMethod”属性的方法。

   2、使用“TestMethod”属性,它能正确显示在菜单上使用的方法的名称。

       测试时,简化了业务对象集合的显示,以及简化在集合中选择单独的业务对象。

   3、为了进行测试,你需要实例化一个CustomConsoleTest子类(这个类是抽象的)和调用方法“Execute”方法。

   在这个简单的类中,神奇之处是一个测试类可以嵌入其他的测试类,执行父类中的测试方法能调用子类的Execute方法。因此,你能很快建立一个分层控制台菜单,用于在整合之前专注于核心业务功能的测试。

  实现:

  所有的测试类都要继承CustomConsoleTest。下面是构造函数:

CustomConsoleTest
  public  CustomConsoleTest()
        {
            var t 
=   this .GetType();
            
int  i  =   ;
            
string  name  =   null ;
            
//  find the methods
             foreach  (var m  in  t.GetMethods(BindingFlags.Public  |  BindingFlags.NonPublic  |  BindingFlags.Instance))
            {
                
if  (IsValidMethod(m,  ref  name))
                {
                    var item 
=   new  MethodItem(name, m);
                    _methods.Add(_menuKeys[i], item);
                    
++ i;
                }
            }
        }

   当CustomConsoleTest子类被实例化,执行以下操作:

   1、使用反射去查找有自定义属性TestMethod的方法。

   2、当一个方法有TestMethod属性,它被添加到一个类名为MethodItem的集合,这个集合包含MethodInfo实例和方法的名称。

   一旦测试方法发现,它将显示在菜单上,允许测试人员通过键盘上的键调用的测试方法。

   为了完成这些,测试人员必须调用方法“Execute”的方法,这个方法浏览方法集合和在菜单上显示测试方法友好名称(如[TestMethod("List Machines")]中就显示List Machines)。

   为了重复的测试,因此能实现重复菜单,测试类可以嵌入其他测试类,调用测试方法也将调用嵌入类的"Execute"。代码如下:

Execute
  public   void  Execute()
        {
            
char  c  =   ' * ' ;
            
while  ((c  !=   ' q ' &&  (c  !=   ' Q ' ))
            {
                
foreach  (var p  in  _methods)
                    Console.WriteLine(
" {0} - {1} " , p.Key, p.Value.DisplayText);
                Console.WriteLine(
" q - Quit " );
                Console.WriteLine();
                c 
=  Console.ReadKey( true ).KeyChar;
                
if  (_methods.ContainsKey(c))
                    
try
                    {
                        var m 
=  _methods[c];
                        m.Method.Invoke(
this null );
                    }
                    
catch  (Exception ex)
                    {
                        Console.ForegroundColor 
=  ConsoleColor.Red;
                        Console.WriteLine(
" An exception has occured when invoking the method {0} " ,
                            ex);
                    }
            }
        }

   为了方便地显示和选择集合中的项,我添加一个泛型函数来简化集合处理,这假设集合实现了通用接口IEnumerable<T>。 

DisplayCollection
protected   void  DisplayCollection < T > (IEnumerable < T >  aCollection)
{
    
if  (aCollection.Count()  ==   )
        Console.WriteLine(
" No item in the collection " );
    
int  i  =   1 ;
    _elts.Clear();
    
foreach  (var e  in  aCollection)
    {
        Console.WriteLine(
" {0} - {1} " , i, GetDescription(e));
        _elts.Add(i, e);
        
++ i;                
    }
    Console.WriteLine();
}

   SelectItem方法使在控制台模式下选择测试方法更加容易:

代码
   protected  T SelectItem < T > (IEnumerable < T >  aCollection)  where  T :  class
        {
            DisplayCollection
< T > (aCollection);
            
            
while  ( true )
            {
                Console.WriteLine(
" Type the element number or q to exit " );
                var text 
=  Console.ReadLine();
                
if  ((text  ==   " q " ||  (text  ==   " Q " ))
                    
return   null ;
                
try
                {
                    
int  index  =  Convert.ToInt32(text);
                    
if  (_elts.ContainsKey(index))
                        
return  (T)_elts[index];
                    
else
                        Console.WriteLine(
" The list does not contain that number, try again ! " );
                }
                
catch
                {
                    Console.WriteLine(
" Wrong value " );
                }
            }
        }

   为了显示菜单,我将寻找到的测试方法放在MethodItem类中

代码
  class  MethodItem
    {
        
internal  MethodItem( string  aDisplayText, MethodInfo aMethod)
        {
            DisplayText 
=  aDisplayText;
            Method 
=  aMethod;
        }

        
internal   string  DisplayText {  get private   set ; }

        
internal  MethodInfo Method {  get private   set ; }
    }

 示例:

   将所有的功能展示在一个机器和零配件的示例中,应用程序包括两种类型的业务对象:Machine(机器)和SparePart(零配件)。

 

代码
class  Machine : Asset
{
   //机器类中有添加零配件AddPart方法。CreateInstances静态的构建方法,零配件列表:List<SparePart>
    
internal   void  AddPart(SparePart aPart)
    {
        Parts.Add(aPart);
        AddWeight(aPart.Weight);
    }
    
    
public  Machine( string  aName)
        : 
base (aName)
    {
        Parts 
=   new  List < SparePart > ();
    }
    
    
public   static   void  CreateInstances()
    {
        Machines 
=   new  List < Machine > ();
        
for  ( int  i  =   ; i  <   new  Random().Next( 10 +   1 ; i ++ )
            Machines.Add(
new  Machine( string .Format( " Machine{0} " , i)));
    }
    
    
public   static  List < Machine >  Machines
    {
        
get ;
        
private   set ;
    }
    
    
public  List < SparePart >  Parts {  get private   set ; }
}

 零配件类:

代码
class  SparePart : Asset
{
    
public  SparePart(Machine aMachine,  string  aName,  double  aWeight):
        
base (aName)
    {
        Machine 
=  aMachine;
        Weight 
=  aWeight;
        aMachine.AddPart(
this );            
    }
    
    
public   static   void  CreateInstances()
    {
        Parts 
=   new  List < SparePart > ();
        var random 
=   new  Random();
        
foreach (var m  in  Machine.Machines)
            
for  ( int  i  =   ; i  <  random.Next( 5 ); i ++ )
            {
                var part 
=   new  SparePart(m,  string .Format
                    (
" {0}-part{1} " , m.Name, i),
                    random.NextDouble());
                Parts.Add(part);
            }
    }
    
    
public  Machine Machine {  get private   set ; }
    
    
public   static  List < SparePart >  Parts
    { 
get private   set ; }
}

 要实现我的测试操作,我需要三个类:MachineTest测试Machine,PartTest测试spare部分,SoftwareTest中嵌入这两个的测试。

代码
///   <summary>
///  this class is used to test software
///   </summary>
class  SoftwareTest : CustomConsoleTest
{
    
///   <summary>
    
///  machine test
    
///   </summary>
     private  MachineTest _machineTest  =   new  MachineTest();
    
    
///   <summary>
    
///  part test
    
///   </summary>
     private  PartTest _partTest  =   new  PartTest();
    
    [TestMethod(
" Machines " )]        
    
private   void  ExecuteMachineTest()
    {
        _machineTest.Execute();
    }
    
    [TestMethod(
" Parts " )]
    
private   void  ExecutePartTest()
    {
        _partTest.Execute();
    }
    
    
static  SoftwareTest()
    {
        Machine.CreateInstances();
        SparePart.CreateInstances();
    }
}

 

代码
class  MachineTest : CustomConsoleTest
{
    [TestMethod(
" List Machines " )]
    
private   void  ListMachines()
    {
        DisplayCollection
< Machine > (Machine.Machines);
    }
    
    
protected   override   string  GetDescription( object  e)
    {
        
if (e  is  Machine)
        {
         var m 
=  e  as  Machine;
         
return   string .Format( " Machine {0} | {1:0.00} | Parts : {2} " ,
             m.Name, m.Weight, m.Parts.Count);
        }
        
return   base .GetDescription(e);
    }
}

  为了更精细显示的业务对象,我重写的方法GetDescription,它本身是以ToString方法为基础。

代码
class  PartTest : CustomConsoleTest
{
    
protected   override   string  GetDescription( object  e)
    {
        
if  (e  is  SparePart)
        {
            var p 
=  e  as  SparePart;
            
return   string .Format( " Machine : 
                { -  { 1 } " , p.Machine.Name, p.Name);
        }
        
return   base .GetDescription(e);
    }
    
    
///   <summary>
    
///  selection
    
///   </summary>
    [TestMethod( " Select A Part " )]
    
protected   void  TestSelect()
    {
        var p 
=  SelectItem < SparePart > (SparePart.Parts);
        
if  (p  !=   null )
            Console.WriteLine(
" You have selected the part {0} " , p.Name);
    }
}

 总结:

    以我个人开发大型软件系统的经验,自动化测试是很好,但还往往不够,在非常特别的情况,要耗很多时间去测试。我的做法很重要一点是没有软件界面部分,在控制台项目中进行测试非常的快。

参考代码:/Files/zhuqil/TestBO.zip

参考原文:http://www.codeproject.com/KB/cs/SimpleConsoleTestClass.aspx


原文链接: http://www.cnblogs.com/zhuqil/archive/2010/02/02/TestBO.html

你可能感兴趣的:(超级简单:一个超轻量级的测试平台)