2015-08-21 207 views
0

我有一个遗留代码,其中包含的每个方法的平均长度大约为200-300行,只有8700多行。我想知道如何才能够覆盖大部分代码,甚至能够获得成功结果的输出。如何测试大型复杂代码?

例如:

public class Aviation implements FlightStruct, FlightSystem extends Vehicles { 
    ...var decoration... 
    ...override method definitions... 

    public controls aviation(Flight request, Flight destination, Flight target) { 

    FlightResponse flightResponse = new FlightResponse(); 

    flightResponse.speed = target.speed; 
    angularVel.calculate(greatCircle.flightResponse.speed); 
    _readVelData(); 
    if (vel.circular.velocity <= 10000) { 
    for(countryName : country) { 
    calculateArrivalTime(); 
    _readFuel(); 
    if(fuel.limit < pwi.percent*100) { 
      createShortPathLandingPoint(Path newDestination, Register checkPoint); 
     } 
    } 
} 
    private eqiptment company(String name, Vendor vendor) { 
    ....calculate equiptment stuff that calls bunch of private methods 
    } 

...alot more method here 
    } 
} 

我不知道我应该如何去进行单元测试这些样的代码? 我试图存根数据,但内部方法太复杂,无法通过。通常一个方法调用多层子方法。 例如.... 燃料类调用_readfuel并在_readfuel中调用destinationCheckPoint,flightWayPoint,flightSpeedCalc ...然后在每个子方法中调用它自己的子方法。 (例如flightWaypoint调用calculateWayPoint,angVelocity,speedLimit,...) 总体而言,我认为这是不可行的,因为找到正确的数据很困难。 然后我的第二个选择是使用Mockito来模拟方法/类。这听起来更合理,但我再次遇到有多个子方法,哪些是私有方法......并且mockito不能嘲笑私有方法。然后我试图使用PowerMockito嘲笑它几千行工作的私有方法并有这样的事情:

while (!report.successful && !telCList.isEmpty()) { 
... 
report.wayPoint = update.assignWayPoint(wayPointList); //null point exception 
... 

,其中更新是零和几百行上面被宣布

UpdateGateWay update = setDestinationWayPoint(distance, speed, altitude); 

setDestinationWayPoint是一个私有方法,我使用powerMockito模拟行为,我也返回值作为存根的一部分。但是当它在下面被调用时它变为空。

我几乎放弃了这一点。我想知道是否有更合理的方式来做这种类型的测试....

回答

1

这是一个艰难的。这个答案几乎肯定会过度简化你试图覆盖的问题的性质,但希望它能指出你正确的方向。一般来说,如果我的任务是使用测试来覆盖此代码,那么我首先会试图追踪一些软件需求,这些需求会全面描述软件的预期行为。然后,我会编写一些高级集成风格的测试,以涵盖预期的行为以及所有记录的边缘案例。我在这个测试中使用的唯一存根将用于调用外部系统。在这个阶段我不会真的用单元测试术语思考。

这里的诀窍就是考虑一下你正试图用一系列谨慎的功能进行测试的软件。一旦你确定了这些组,然后为每个组编写测试就成为一个识别功能边界的问题,然后通过集成测试来执行这些功能。基本上,在这个阶段,你应该关心的是系统(或函数,如果粒度级别有意义)的输入和输出,而不是过于关心具体的实现细节。一个例子可能是:“当我调用这个函数时,我期望在数据库中有这些确切属性的新记录。”

在一个复杂的系统中,您可以期待在集成测试中看到一些重叠,但在此阶段,这没关系。你所要做的就是确保当你将这些长功能重构为更小的单位时,系统的功能不会以非预期的方式(或中断)改变。换句话说,您要确保在开始重构这些作品之后系统的行为与开始之前的完全相同。

一旦您的集成测试到位,希望您已经了解系统(或系统的一部分)的行为,以便您可以在您参考的大型复杂功能中识别出谨慎的行为单元你的问题。这也将使您有机会提取可能存在的任何重复。您的集成测试涵盖的行为和边缘案例越彻底,您就越有信心进行重构。

一旦你到了这个阶段,你开始识别单个功能单元,然后开始考虑单元测试是有意义的。这也是模拟和存根对于测试内部API(即除系统间API边界之外嘲弄域间API边界)更有意义的阶段。

再次 - 这是一个巨大的过度简化了一个话题,我确信整本书已经写入了,但我希望这可以帮助您获得一些高层次的定向洞察力。

2

写作很有价值de novo对你不了解的遗留代码库进行单元测试是一项艰巨的任务。特别是,如果代码是一团糟;例如“每种方法平均约200-300行”

问题是如何找出方法>>应该用< <以足够的准确度来编写单元测试。不幸的是,这往往需要更深入的了解,而不仅仅是了解代码如何写作的行为。

这不是一个不可能完成的任务,但它可能需要很多工作......取决于代码的内在复杂性,原始设计的质量,需求,问题域等等。

另一种可能的选择是将代码库视为黑盒子。不要编写单元测试,而要编写基于许多“典型”场景的系统测试;例如如果您从数据库状态X开始并提供输入I,则预期输出O和新数据库状态X'。每次您需要对旧版代码库进行更改时,请添加新的系统测试。


我想知道我怎么能够覆盖大部分的代码,甚至获得成功结果的输出。

我想你在这里有一些误解。

  • 单元测试时,覆盖代码的百分比不如人们预期的重要。目标应该是涵盖错误可能存在的代码。

  • “成功结果”是什么?个别方法调用?假设你知道结果应该是。这可能是困难的,特别是如果该方法有副作用,你不知道或不明白。


最后一点是,一些代码本质上是很难写单元测试。通常,代码应该被重写。一个8,700行的代码库(实际上)不是那么大,并且可能成为重写的候选对象。 (您可以将现有代码视为“工作原型”......但是没有一套体面的功能要求)。