marcus1877 发表于 2013-7-23 16:40:25

第一个UT测试-增加注释

本帖最后由 marcus1877 于 2013-7-30 09:28 编辑

公司开始准备上UT测试,但是测试与开发都没经验。我与另一个developer engineer结对写UT,他进入公司前接触过,但是他的经验有限,无法从我的角度考虑UT,也不知道怎么指导我。
我的知识水平也就把【.NET单元测试艺术】(Roy Osherove)这本书看了一遍,所以这次有很多疑问,想问问资深的UT人员。
先写一个基础问题,如何写下面代码的UT

开发工具、环境:VS2012 ultimate,c#,Win8,Windows Store APP
源码类类似:
Class DataShare:IDataShare
{
   bool Disposed;    //这里是private的field,而且没有对应的property
   Manager Manager=null;//这里是private的field,而且没有对应的property
   public pubMethod1(IEnumerable<StorageFile> files)   //IEnumerable是系统的,StorageFile也是系统内置
    {
          if (Disposed)
         {
             Logger.log("error","");
             throw new objectException();
          }
          if (files !=null && files.count()>0)
         {
             flag=true;
             Manager.StartFiles(files);
         }else
      {
             flag=false;
      }
      return flag;
   }

.........其他代码略.....
   } //Class 结束
Class Manager
{
          internal async void StartFiles(IEnumerable<StorageFile> files)
      {
            ................      
      }
}

我的第一个问题是:
    这里Manager是一个private field,无法设置,如果要做真正的UT,我个人认为必须把它变成internal的(或增加对应internal的属性),同时要把它的interface都抽取出来,这样我才能自己做一个stub对象,完成UT??

第二个问题是 :
    StartFiles被开发人员定义为一个internal的method,那要为了做DataShare的UT,就必须把它改成Public的??这样extract interface后我的stub对象就会有startFiles方法了。
    如果开发人员不把它抽取接口,那我就写不出完整的stub类了,因为不包含这个StartFiles方法!!!!!
               
如果要改public,那么开发人员用什么原则确保哪些method该改成public,哪些可以保持internal?

goal1860 发表于 2013-7-25 07:16:56

public还是internal取决于外部调用的需要,而不是UT的需要,一切internal/private的方法变量都是不能直接测试的,但是假如它们没有最终被public的方法调用到就是无用代码,应当删除。
总的来讲没有开发经验去写UT是比较困难的。尤其是web服务层的ut要mock的东西比较多。

marcus1877 发表于 2013-7-30 09:53:30

回复 2# goal1860
感谢答复,等了很久。这里我列出来的还是一个简单的情况。按照UT理论,上面Manager必须是一个stub类,可是如果StartFiles是internal的,Stub类就不能含有StartFiles方法了,那就无法UT了。

更详细复杂一些的情况是Public方法里调用很多Private方法,而Private方法又使用了很多依赖项,要想做UT,就必须全部隔离出来,那么随情况可能就会有很多隔离项。比如
Public SendCommond(IControlCommand oCommand)
{
    Packet packt=null;
    switch(oCmmand.Type)
    {
          case ControlCommandType.connecting
               packet=CreateConnectingPacket(oCommand)
          break;
          case ControlCommandType.connecting
               packet=new CommondPacket(oCommand);
               beginReceive();
          break;
         default:
            packet=new CommonPacket(oCommand);
         break;
   }
    if (packet !=null)
    {
         PacketQueueManager.SendQueue(packet);
         return true;
   }else
    {
          return false;
   }
}
PacketQueueManager PacketQueueManager=null;
packet CreateConnectingPacket(IControlCommand ocommand)
{ .............
}
beginReceive ()
{...................
}
为了做UT,我是不是就得把这些private方法想办法隔离出去??如果是普通项目,也许用VS的Fakes框架的shim方法就能规避,可是Windows Store APP中是无法使用Fakes框架及任何隔离工具的,比如
Rihino,Typemocker。各位高手怎么看呢?
难道是开发人员把这些方法写成virtual的,然后我的test类继承?这种方式是书里提到的一种方式。
说实话,开发人员是否愿意做这些改变我还真比较担心,因为这些可能就是要实施UT必须的,搞不好就推不动。必须在可测试与以前常规方法之前取舍一个。

goal1860 发表于 2013-7-31 09:29:59

我看的很混乱,StartFiles假如是internal的,那怎么能用Manager.StartFiles调用呢?这和Manager是不是stub貌似没有关系。
Manager对象我猜测是由依赖注入模式控制的,所以才需要做stub,假如是这样的话DataShare就不能用常规构造器来产生,通常应该有相应的服务返回一个DataShare实例。
从你的描述来看系统设计的耦合度很高,造成UT困难,因为牵扯的依赖太多了。一般要开发改设计是不太可能的。如果可能的话你应当建议由系统的设计和编码人员来做UT,最好是做TDD。

marcus1877 发表于 2013-7-31 10:01:50

本帖最后由 marcus1877 于 2013-7-31 10:10 编辑

回复 4# goal1860
非常感谢,StartFiles是Manager类的一个方法,声明为internal,所以可以Manager实例调用。由于UT代码里要调用StartFiles,自然要把Manager做成Stub,才能调用假的StartFiles。这段 在1楼上有写出来。

因为都没做过UT,所以确实头疼...

marcus1877 发表于 2013-7-31 15:29:02

找到一篇E文,与我第二个问题相似,个人认为开发环境是VS+C#时都可以这么考虑。

http://programmers.stackexchange.com/questions/188609/best-way-to-unit-test-methods-that-call-other-methods-inside-same-class

marcus1877 发表于 2013-7-31 15:38:37

找到一个E文参考,正好是C#的,应该可以按他们的方法修改
http://programmers.stackexchange.com/questions/188609/best-way-to-unit-test-methods-that-call-other-methods-inside-same-class

marcus1877 发表于 2013-7-31 15:40:33

最佳答案目前只能选择4楼。

marcus1877 发表于 2013-7-31 15:42:32

目前只能选择4楼作为答案

marcus1877 发表于 2013-7-31 16:40:23

目前最佳答案只能给4楼了。

marcus1877 发表于 2013-7-31 16:44:36

目前最佳答案只能给4楼了。
页: [1]
查看完整版本: 第一个UT测试-增加注释