java - 无论测试扩展如何,Jenkins 中的 Cobertura 代码覆盖率报告似乎都会提供相同的统计数据

标签 java jenkins testng code-coverage cobertura

问题

无论运行多少个测试,从我的脚本和 Cobertura 创建的代码覆盖率报告似乎都会给我相同的覆盖率。如果我运行所有测试或不运行任何测试,Jenkins 的 Cobertura 插件总是向我显示相同的图表。

问题描述

我是 Jenkins、Cobertura 和整个代码覆盖率的初学者。我有一个简单的 Java 项目(请参见下面的代码:程序),它在 TestNG 中运行一些简单的测试(请参见下面的代码:测试)。一些脚本正在处理编译和测试(请参阅下面的代码:脚本),在 Jenkins 的构建:执行 Shell 中提供给 Jenkins 的脚本按以下顺序:

  • clean.sh
  • 编译.sh
  • 测试覆盖率.sh
  • (test.sh)

现在,在 test-coverage.sh 中,Cobertura 检测程序的源代码(代码:程序 部分),创建一个 coverage.xml 应该(并且看起来确实)包含运行测试后我的代码的覆盖范围。

但是,如果我应该删除一个测试(或全部,这并不重要)并再次运行它,coverage.xml 似乎会提供与我运行所有测试相同的统计信息测试完好无损。

我做错了什么?我误解了这个概念吗?我还应该检测我的测试类吗?

代码:程序

该程序是 FizzBu​​zz 数学游戏的自动化、简单版本,每个玩家轮流计数,用所述均匀面值替换每个数字分母为另一个单词(例如“Fizz”,但在本例中为“Cowabunga”)。

main.java

public class main {
    public static void main(String[] args){
        StartGame game = new StartGame();
        game.start();
    }
}

StartGame.java

public class StartGame {
    public static void start() {
        GameRules gr = new GameRules(3,5);
        gr.startGame();
    }
}

GameRules.java

public class GameRules {
    private int currentNumber;
    private int currentPlayer;
    private int playUntilThisNumber;
    private int dividerToSkip;
    private List<Player> playerlist;


    public GameRules(int numberOfPlayers, int divider){
        playerlist = new ArrayList<Player>();
        currentNumber=1;
        currentPlayer=3;
        playUntilThisNumber = 100;
        dividerToSkip = divider;
        fillPlayerList(numberOfPlayers);
    }

    public int getCurrentNumber() {
        return currentNumber;
    }

    public void increaseCurrentNumber() {
        this.currentNumber++;
    } 

    private void fillPlayerList(int numberOfPlayers){
        for(int i = 0 ; i < numberOfPlayers ; i++){
            Player p = new Player(this, i+1,dividerToSkip);
            playerlist.add(p);
        }
    }

    public void startGame(){
        while(currentNumber<=playUntilThisNumber){
            System.out.println(playerlist.get(currentPlayer++%playerlist.size()).play());
        }
    }
}

Player.java

public class Player {
    private GameRules rules;
    private int dividerToSkip;
    private int playerNumber;


    public Player(GameRules r, int playerNr, int divider){
        rules = r;
        dividerToSkip = divider;
        playerNumber = playerNr;
    }

    public String play() {
        String s;
        if(rules.getCurrentNumber()%dividerToSkip==0){
            s = "Player " + playerNumber + ": Cowabunga";
        }
        else {
            s = "Player " + playerNumber + ": " +rules.getCurrentNumber();
        }
        rules.increaseCurrentNumber();
        return s;
    }
}

代码:测试

GameRuleTest.java

public class GameRulesTest {
        private int nrOfPLayers;
        private int divider;
        private GameRules gr;

        @BeforeMethod
        public void setUp(){
            nrOfPLayers = 35;
            divider = 13;
            gr = new GameRules(nrOfPLayers,divider);
        }

        @Test
        public void testCurrentNumber(){
            assert (gr.getCurrentNumber()>0);
        }

        @Test
        public void testIncreaseCurrentNumber() {
            int cn = gr.getCurrentNumber();
            gr.increaseCurrentNumber();
            assert(gr.getCurrentNumber()==cn+1);
        } 
    }

PlayerTest.java

public class PlayerTest {
    private int nrOfPLayers;
    private int divider;
    private GameRules gr;
    private Player player;

    @BeforeMethod
    public void setUp(){
        nrOfPLayers = 35;
        divider = 13;
        gr = new GameRules(nrOfPLayers,divider);
        player = new Player(gr,100,divider);
    }

    @Test
    public void testReturnString() {
        String res = player.play();

        assert "Player 100: 1".equals(res) : "Expected correct return string, got " + res;

        assertEquals("Player 100: 1", res);
    }
}

性能测试.java

public class PerformanceTest {
    private StartGame game;

    @BeforeMethod
    public void setUp(){
        game = new StartGame();
    }

    @Test
    public void testExecutionTime(){
        long startTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
        game.start();
        long finnishTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
        System.out.println(startTime);
        System.out.println(finnishTime);
        assert (finnishTime-startTime<0.0000005);
    }
}

代码:脚本

clean.sh

#!/bin/bash

find . -name '*.class' | xargs rm
rm -rf instrumented cobertura.ser cobertura_report test-output

编译.sh

#!/bin/bash
javac src/MyProject/*.java
javac -cp src/:testng-6.8/testng-6.8.jar test/*.java

测试.sh

#!/bin/bash
java -cp testng-6.8/testng-6.8.jar:test:src org.testng.TestNG -verbose 2 test/test.xml

测试覆盖率.sh

#!/bin/bash

COBERTURA=cobertura-2.0.3
INSTRUMENTED=instrumented
REPORTDIR=cobertura_report

mkdir -p $INSTRUMENTED
mkdir -p $REPORTDIR

# Instrument the classes that we want to check coverage on
$COBERTURA/cobertura-instrument.sh src/MyProject/*.class --destination $INSTRUMENTED || exit

# Run the tests
java -cp $COBERTURA/$COBERTURA.jar:$INSTRUMENTED:testng-6.8/testng-6.8.jar:test org.testng.TestNG -verbose 2 test/test.xml

# Generate report
$COBERTURA/cobertura-report.sh --format xml --destination $REPORTDIR src

# Check coverage
#$COBERTURA/cobertura-check.sh --branch 0

测试.xml

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > 
<suite name="MindGameSuite" verbose="1" >
  <test name="MindGame"   >
    <classes>
      <class name="PlayerTest" />
      <class name="GameRulesTest" />
      <class name="PerformanceTest" />
    </classes>
 </test>
</suite>

coverage.xml(生成时)

<?xml version="1.0"?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">

<coverage line-rate="0.8888888888888888" branch-rate="1.0" lines-covered="32" lines-valid="36" branches-covered="6" branches-valid="6" complexity="1.3333333333333333" version="2.0.3" timestamp="1412942052582">
    <sources>
        <source>src</source>
    </sources>
    <packages>
        <package name="MyProject" line-rate="0.8888888888888888" branch-rate="1.0" complexity="1.3333333333333333">
            <classes>
                <class name="MyProject.GameRules" filename="MyProject/GameRules.java" line-rate="1.0" branch-rate="1.0" complexity="1.4">
                    <methods>
                        <method name="&lt;init&gt;" signature="(II)V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="23" hits="1" branch="false"/>
                                <line number="24" hits="1" branch="false"/>
                                <line number="25" hits="1" branch="false"/>
                                <line number="26" hits="1" branch="false"/>
                                <line number="27" hits="1" branch="false"/>
                                <line number="28" hits="1" branch="false"/>
                                <line number="29" hits="1" branch="false"/>
                                <line number="30" hits="1" branch="false"/>
                            </lines>
                        </method>
                        <method name="fillPlayerList" signature="(I)V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="50" hits="4" branch="true" condition-coverage="100% (2/2)">
                                    <conditions>
                                        <condition number="0" type="jump" coverage="100%"/>
                                    </conditions>
                                </line>
                                <line number="51" hits="3" branch="false"/>
                                <line number="52" hits="3" branch="false"/>
                                <line number="54" hits="1" branch="false"/>
                            </lines>
                        </method>
                        <method name="getCurrentNumber" signature="()I" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="36" hits="180" branch="false"/>
                            </lines>
                        </method>
                        <method name="increaseCurrentNumber" signature="()V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="43" hits="100" branch="false"/>
                                <line number="44" hits="100" branch="false"/>
                            </lines>
                        </method>
                        <method name="startGame" signature="()V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="61" hits="101" branch="true" condition-coverage="100% (2/2)">
                                    <conditions>
                                        <condition number="0" type="jump" coverage="100%"/>
                                    </conditions>
                                </line>
                                <line number="62" hits="100" branch="false"/>
                                <line number="64" hits="1" branch="false"/>
                            </lines>
                        </method>
                    </methods>
                    <lines>
                        <line number="23" hits="1" branch="false"/>
                        <line number="24" hits="1" branch="false"/>
                        <line number="25" hits="1" branch="false"/>
                        <line number="26" hits="1" branch="false"/>
                        <line number="27" hits="1" branch="false"/>
                        <line number="28" hits="1" branch="false"/>
                        <line number="29" hits="1" branch="false"/>
                        <line number="30" hits="1" branch="false"/>
                        <line number="36" hits="180" branch="false"/>
                        <line number="43" hits="100" branch="false"/>
                        <line number="44" hits="100" branch="false"/>
                        <line number="50" hits="4" branch="true" condition-coverage="100% (2/2)">
                            <conditions>
                                <condition number="0" type="jump" coverage="100%"/>
                            </conditions>
                        </line>
                        <line number="51" hits="3" branch="false"/>
                        <line number="52" hits="3" branch="false"/>
                        <line number="54" hits="1" branch="false"/>
                        <line number="61" hits="101" branch="true" condition-coverage="100% (2/2)">
                            <conditions>
                                <condition number="0" type="jump" coverage="100%"/>
                            </conditions>
                        </line>
                        <line number="62" hits="100" branch="false"/>
                        <line number="64" hits="1" branch="false"/>
                    </lines>
                </class>
                <class name="MyProject.Player" filename="MyProject/Player.java" line-rate="1.0" branch-rate="1.0" complexity="1.5">
                    <methods>
                        <method name="&lt;init&gt;" signature="(LMyProject/GameRules;II)V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="20" hits="3" branch="false"/>
                                <line number="21" hits="3" branch="false"/>
                                <line number="22" hits="3" branch="false"/>
                                <line number="23" hits="3" branch="false"/>
                                <line number="24" hits="3" branch="false"/>
                            </lines>
                        </method>
                        <method name="play" signature="()Ljava/lang/String;" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="32" hits="100" branch="true" condition-coverage="100% (2/2)">
                                    <conditions>
                                        <condition number="0" type="jump" coverage="100%"/>
                                    </conditions>
                                </line>
                                <line number="33" hits="20" branch="false"/>
                                <line number="36" hits="80" branch="false"/>
                                <line number="38" hits="100" branch="false"/>
                                <line number="39" hits="100" branch="false"/>
                            </lines>
                        </method>
                    </methods>
                    <lines>
                        <line number="20" hits="3" branch="false"/>
                        <line number="21" hits="3" branch="false"/>
                        <line number="22" hits="3" branch="false"/>
                        <line number="23" hits="3" branch="false"/>
                        <line number="24" hits="3" branch="false"/>
                        <line number="32" hits="100" branch="true" condition-coverage="100% (2/2)">
                            <conditions>
                                <condition number="0" type="jump" coverage="100%"/>
                            </conditions>
                        </line>
                        <line number="33" hits="20" branch="false"/>
                        <line number="36" hits="80" branch="false"/>
                        <line number="38" hits="100" branch="false"/>
                        <line number="39" hits="100" branch="false"/>
                    </lines>
                </class>
                <class name="MyProject.StartGame" filename="MyProject/StartGame.java" line-rate="1.0" branch-rate="1.0" complexity="1.0">
                    <methods>
                        <method name="&lt;init&gt;" signature="()V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="6" hits="1" branch="false"/>
                            </lines>
                        </method>
                        <method name="start" signature="()V" line-rate="1.0" branch-rate="1.0">
                            <lines>
                                <line number="8" hits="1" branch="false"/>
                                <line number="9" hits="1" branch="false"/>
                                <line number="10" hits="1" branch="false"/>
                            </lines>
                        </method>
                    </methods>
                    <lines>
                        <line number="6" hits="1" branch="false"/>
                        <line number="8" hits="1" branch="false"/>
                        <line number="9" hits="1" branch="false"/>
                        <line number="10" hits="1" branch="false"/>
                    </lines>
                </class>
                <class name="MyProject.main" filename="MyProject/main.java" line-rate="0.0" branch-rate="1.0" complexity="1.0">
                    <methods>
                        <method name="&lt;init&gt;" signature="()V" line-rate="0.0" branch-rate="1.0">
                            <lines>
                                <line number="7" hits="0" branch="false"/>
                            </lines>
                        </method>
                        <method name="main" signature="([Ljava/lang/String;)V" line-rate="0.0" branch-rate="1.0">
                            <lines>
                                <line number="14" hits="0" branch="false"/>
                                <line number="15" hits="0" branch="false"/>
                                <line number="16" hits="0" branch="false"/>
                            </lines>
                        </method>
                    </methods>
                    <lines>
                        <line number="7" hits="0" branch="false"/>
                        <line number="14" hits="0" branch="false"/>
                        <line number="15" hits="0" branch="false"/>
                        <line number="16" hits="0" branch="false"/>
                    </lines>
                </class>
            </classes>
        </package>
    </packages>
</coverage>

最佳答案

谜团解开了。

检测类覆盖测试类完成的所有阅读。这意味着来自这些的任何调用都将导致(如果有的话)覆盖。 在本例中,我运行了几个单元测试,在生成代码后,我编写了 PerformanceTest.java 只是为了尝试更高级别的测试。

现在的问题是性能测试运行程序来计算执行时间。这意味着在此测试期间几乎运行了程序的整个代码,这使得单元测试变得过时,并且单元测试应该涵盖的内容无论如何都被涵盖了。

总结

使用较小的测试,例如单元测试模块测试来查看代码覆盖率。当要查看一行代码是否运行时,较高级别的测试会使大多数较小的测试变得过时。

关于java - 无论测试扩展如何,Jenkins 中的 Cobertura 代码覆盖率报告似乎都会提供相同的统计数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26296185/

相关文章:

java - 如何在 TestNG 测试用例中使用 Governator 注入(inject)依赖项?

java - 向 KeyListener 添加计时器

java - 选择正确的枚举

java - 完美数字显示 Java

jenkins - Gitleak 扫描 - 避免以前的提交

linux - 无法将 slave.jar 复制到 slave 上的 '/home/jenkins'

java - 如果 TestNG 不尝试运行 JUnit 测试,我的 Maven 项目如何同时运行 JUnit 和 TestNG?

java - Java Applet 安全警告 "JAR file manifest does not contain the Permissions attribute"是什么意思?

linux - 当 jenkins.war 部署在 tomcat webapps 文件夹中时,Jenkins 电子邮件通知错误?

java - 如何断言或验证单元测试中 "almost"相同的 2 段文本