编写JUnit测试以获得更复杂的方法

时间:2014-05-22 18:21:36

标签: java unit-testing testing junit

我正在学习JUnit测试程序,并且已经完成了我能找到的所有教程。我理解实现基本测试的概念,例如当一个方法需要一些输入并使用它们来计算返回值时。在这种情况下,编写一个断言预期返回值应该是什么的测试很简单。

但是,当有更复杂的测试方法时,如果使用从Random对象生成的数字,或者如果需要测试不返回值的方法,那会怎样做?

例如,我有以下方法可以编写测试:

// Requirement 9.5.0
public void firePhotonTorpedos(TrekGUI gui, Starship target, int score)
{
    // Requirement 9.5.1.1
    // Torpedoes can either hit or miss.
    Random rand = new Random();
    boolean hit = false;
    int isItAHit= rand.nextInt(10);;
    int damage = 0;


    //80 % chance of hitting target
    if(isItAHit < 8)
    {
        hit = true;
    }

    // if it was a hit, calculate damage
    if(hit)
    {
        //damage between 30 and 50
        damage = rand.nextInt(21) + 30;

        target.setHitPoints(target.getHitPoints() - damage);
    }

    this.setPhotonTorpedos(this.getPhotonTorpedos() - 1);
    // Requirement 5.4.1
    this.setEnergy(this.getEnergy() - 50);


    //if target hit but not destroyed, say so
    if(hit && target.getHitPoints() > 0)
    {
        gui.getBottomPanel().getConsole().append("\nVessel at " + target.getQuadrantLocation() + ", " + target.getSectorLocation() + " damaged.");
        gui.getBottomPanel().getConsole().append("\n" + damage + " units damage."); 
    }
    //if target destroyed, say so and remove its ship
    // Requirement 6.2.0
    else if(hit && target.getHitPoints() < 0)
    {
        //warship is gone, so set its contains field to false and remove its icon
        target.getSectorLocation().setContainsWarship(false);
        target.getSectorLocation().getSectorView().hideWarshipIcon();

        klingonsLeft = klingonsLeft - 1;

        gui.getBottomPanel().getConsole().append("\n" + damage + " units damage.");
        gui.getBottomPanel().getConsole().append("Vessel at " + target.getQuadrantLocation() + ", " + target.getSectorLocation() + " destroyed.");

        // Increase score (bonus depending on how much energy is left)
        int bonus = this.getEnergy()/20;
        score = score + (50 + bonus);
    }
    //if it was a miss, say so
    else
    {
        gui.getBottomPanel().getConsole().append("Torpedo missed.");
    }
}

我将如何为此编写正式测试?由于代码中有三个谓词语句,我知道代码中应该有三条不同的路径:

火鱼雷&gt;点击(否)&gt;端

火鱼雷&gt;点击(是)&gt;计算损害&gt;被毁坏(不)&gt;容器保持&gt;端

火鱼雷&gt;点击(是)&gt;计算损害&gt;被毁坏(是)&gt;船离开&gt;端

因此,如果我想获得完整的路径覆盖,我需要编写至少三个表达这些条件的测试用例。很容易看出这些价值观是什么。但是我如何使用它们来编写测试?例如,JUnit可以访问我的代码中的布尔命中值,以便我可以在不使用随机数的情况下定义值吗?我知道我可以通过代码来查看它是否正常工作(我有),但我被告知我应该使用更正式的测试手段,比如JUnit。

我希望这些问题有意义。我只是希望更好地了解在现实世界中如何进行这种测试。

1 个答案:

答案 0 :(得分:3)

  

然而,当有更复杂的测试方法时,人们会怎么做,例如可能使用从Random对象生成的数字的方法

您找到了控制Random的方法。例如,您可以将其传递给方法,并且具有无参数的重载,如果您确实需要,则使用new Random()调用它。

通过这种方式,您的测试可以传入他们想要的任何Random实例 - 例如一个具有预设种子,或者一个使用模拟框架来允许您控制返回的值。

  

或者是否需要测试不返回值的方法?

你测试副作用。你的方法必须副作用,否则就没有意义了。您只需要一种访问这些副作用的方法(例如,通过调用target.getHitPoints()

现在你的方法似乎有“模型”副作用 UI副作用......而且它也很长。这两个都会让人难以测试。将代码拆分为更小,更集中的方法通常可以更轻松地阅读更容易测试。在这方面,将UI与业务逻辑分开也有帮助。