maven 静态代码

    技术2024-01-07  97

    在整个预测云计算(PCC)项目中使用了多种构建工具和技术,使开发团队能够自动构建和验证更改。 所选择的工具和技术使团队能够自动化构建过程并高度自信地部署更改,使其能够正常运行并受到支持。 在本文中,我们讨论了PCC中使用的构建和测试方法。 图1说明了PCC构建和测试体系结构。

    图1.构建和测试架构

    Maven构建自动化

    构建自动化是一种实践,它消除了构建过程中耗时且容易出错的手动步骤,并用自动且一致的过程代替了这些步骤,这些过程可从源代码中创建工件。 构建自动化工具通过卸载和自动化构建过程来促进小变化的测试和传播。 PCC使用Maven作为其构建自动化工具。 使用Maven,开发人员可以签入源代码管理中的更改,在这些更改中自动测试代码并将其内置到可以部署的工件中。

    成绩单

    Maven通过使用定义构建过程的XML文件来执行构建和测试。 Maven是一个高度结构化的构建系统,可在项目之间实施统一性。 这使开发人员可以在不同项目之间移动,并具有与其构建系统一致的界面。

    PCC系统中使用的工具和技术使团队能够自动化构建过程并部署更改,并充满信心地认为代码将是可运行且可支持的。

    Maven是基于生命周期的。 构建生命周期包括以下高级步骤。 如果这些步骤中的任何一个失败,则将不执行下一步,并且将生成错误日志。

    验证 。 验证执行构建生命周期的所有信息均可用并且在语法上正确。 生成源。 生成项目所需的任何源代码。 在PCC项目中,该步骤用于使用OpenJPA提供程序自动生成与数据存储访问相关的某些代码段。 流程源。 在编译之前,检查源代码并对其执行转换步骤。 生成资源和过程资源 。 类似于其相应的源步骤,但是对资源(例如XML文件)而不是代码起作用。 编译 。 将源代码编译成可执行代码。 流程测试源和流程测试资源 。 预处理测试配置以设置测试框架和上下文。 测试编译 。 将测试编译为可执行代码。 测试 。 执行测试。 包装 获取编译后的代码并将其放入可分发格式,例如Java存档(JAR)。 安装 。 将打包的代码安装到本地存储库中。 安装后,该JAR适合用作其他相关项目的依赖项。 部署 。 将工件推送到生产和开发环境以提供PCC操作。

    表1列出了Maven构建过程中的所有项目及其支持PCC的功能。

    表1. Maven构建过程中涉及的项目
    项目 功能 大引擎 对大数据执行多线程作业运行,为RESTful服务建立端点,并为WebSphere Liberty Profile进行配置。 设定档 解析应用程序,网球,高尔夫和整体比赛配置。 因素 高尔夫和网球模拟中的特征提取算法。 坚持不懈 处理数据库功能。 球拍流 提取InfoSphere Streams中的情感分析。 播放器 包含有关玩家的信息。 共享 包含PCC项目之间共享的元素。 共享集成 为共享项目提供集成测试。 Twitter分析 对鸣叫量进行分析。 推特出口商 将Twitter feed导出到其他InfoSphere Steams流程。 推特 从Twitter API读取。 WebLogAnalyzer 分析Web日志内容以从内容中提取玩家提及。 视觉效果 支持从前端视觉到后端BigEngine系统的请求代理的系统。

    图2中的BigEngine项目是PCC的主要工作。 这项工作建立了PCC用来预测站点流量的分析和决策引擎。 BigEngine-Integration作业对BigEngine执行耗时的集成测试,并与BigEngine作业隔离开来,以加快小更改的交付时间。 TwitterAnalysis,TwitterExporter和StreamsLogAggregator作业用于创建工件以在InfoSphere Streams中进行部署。 其他项目,例如WebLogAnalyzer,提供了支持PCC的大数据功能。 这些都是基于Maven的项目。

    图2. PCC基于Jenkins Maven的构建作业

    清单1包含BigEngine项目的Maven配置指令。 指令包含在称为项目对象模型(POM)文件的文档中。 该文件包含在PCC中的BigEngine项目上编译,测试和执行静态分析的所有必要指令。 POM文件包含离散的部分,包括版本,模型版本,包装,属性和内部版本。 version指令定义了由Maven构建的项目的当前版本。 packaging部分告诉Maven在编译和测试后如何packaging生成的工件。 modelVersion通知Maven正在使用Maven模型的哪个版本。 在大多数情况下,该值为4.0.0。 artifactID是没有文件扩展名的打包产品的名称。 POM文件是一个非常大的连续文件。 出于说明目的,将其分为以下几个单独的部分。

    清单1. BigEngine Maven配置
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>BigEngine</artifactId> <version>2.0.0</version> <packaging>war</packaging>

    properties部分定义了Maven在构建过程中要使用的几个属性。 对于此特定项目,我们设置输出目录和构建时间指令,默认情况下,我们跳过此构建的集成测试。 源和输出编码也由Maven设置和使用。

    清单2. BigEngine Maven属性配置
    <properties> <output.directory>${project.basedir}\WebContent\WEB-INF\classes</output.directory> <buildtime>${maven.build.timestamp}</buildtime> <skip.integration.tests>true</skip.integration.tests> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties>

    build部分包含本文档中的大多数指令,并控制Maven如何构建该项目。 本节有几个小节:资源,测试资源,插件,插件管理,父项和依赖项。 除了小节外, build配置还包含用于指导构建过程的指令。 finalName指令为Maven提供一个名称,以用于生成的构建工件。 这通常与更高级别项目配置中的工件ID相同。 该directory是Maven放置构建工件的位置。 outputDirectory是Maven放置编译结果的位置。 testOutputDirectory是已编译测试源文件的目标。 此目录是单独的,因此测试文件不会与最终工件打包在一起。

    清单3. BigEngine Maven构建配置
    <build> <finalName>BigEngine</finalName> <directory>target</directory> <outputDirectory>${output.directory}</outputDirectory> <testOutputDirectory>${output.directory}</testOutputDirectory> <sourceDirectory>src/main/java</sourceDirectory>

    resource部分定义了应包含在结果包中的非代码资源的位置。 test-resources部分指示Maven在哪里定位在测试阶段应该可用但未与构建工件打包在一起的资源。

    清单4. BigEngine Maven资源和测试资源配置
    <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <testResources> <testResource> <directory>src/test/resources</directory> </testResource> </testResources>

    plugins指令包含在构建过​​程中使用的Maven插件列表。 每个插件的配置各不相同。 通常,必须始终定义插件的版本。 每个插件可能都有一些可以提供的特定配置指令。 有时,我们定义了在插件的执行阶段。

    清单5. BigEngine Maven插件配置
    <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <id>auto-clean</id> <phase>initialize</phase> <goals> <goal>clean</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> <configuration> <webXml>${output.directory}\web.xml</webXml> <warSourceDirectory>WebContent</warSourceDirectory> <archive> <addMavenDescriptor>false</addMavenDescriptor> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>templating-maven-plugin</artifactId> <version>1.0-alpha-3</version> <executions> <execution> <id>filter-src</id> <goals> <goal>filter-sources</goal> </goals> <configuration> <sourceDirectory>${basedir}/src/main/java-templates</sourceDirectory> <outputDirectory>${project.build.directory}/generated-sources/java-templates</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>1.9.1</version> <executions> <!-- States that the plugin's add-test-source goal is executed at generate-test-sources phase. --> <execution> <id>add-integration-test-sources</id> <phase>generate-test-sources</phase> <goals> <goal>add-test-source</goal> </goals> <configuration> <!-- Configures the source directory of integration tests. --> <sources> <source>src/integration-test/java</source> </sources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.18.1</version> <configuration> <forkCount>4</forkCount> <reuseForks>true</reuseForks> <excludes> <exclude>**/IT*.java</exclude> </excludes> </configuration> </plugin> <!-- Used for integration tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.18.1</version> <configuration> <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory> </configuration> <executions> <!-- States that both integration-test and verify goals of the Failsafe Maven plugin are executed. --> <execution> <id>integration-tests</id> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> <configuration> <skipTests>${skip.integration.tests}</skipTests> </configuration> </execution> </executions> </plugin> </plugins>

    Maven使用pluginmanagement部分来适当地定义应在何处执行某些插件,也称为lifecycle-mapping 。

    清单6. BigEngine Maven插件管理配置
    <pluginManagement> <plugins> <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --> <plugin> <groupId>org.eclipse.m2e</groupId> <artifactId>lifecycle-mapping</artifactId> <version>1.0.0</version> <configuration> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId> org.apache.maven.plugins </groupId> <artifactId> maven-clean-plugin </artifactId> <versionRange> [2.4.1,) </versionRange> <goals> <goal>clean</goal> </goals> </pluginExecutionFilter> <action> <ignore></ignore> </action> </pluginExecution> <pluginExecution> <pluginExecutionFilter> <groupId> com.google.code.maven-replacer-plugin </groupId> <artifactId> replacer </artifactId> <versionRange> [1.5.3,) </versionRange> <goals> <goal>replace</goal> </goals> </pluginExecutionFilter> <action> <ignore></ignore> </action> </pluginExecution> <pluginExecution> <pluginExecutionFilter> <groupId> org.codehaus.mojo </groupId> <artifactId> templating-maven-plugin </artifactId> <versionRange> [1.0-alpha-3,) </versionRange> <goals> <goal>filter-sources</goal> </goals> </pluginExecutionFilter> <action> <ignore></ignore> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata> </configuration> </plugin> </plugins> </pluginManagement> </build>

    parent指令指示Maven该项目具有父POM,并且应从该项目继承详细信息。 这可用于在项目之间共享库和库版本。 artifactID告诉Maven该artifactID的名称,以便Maven可以搜索适当的项目。 version告诉Maven应该使用哪个版本的artifactID 。 groupId是用于对项目进行分组的Java样式的点分名称。 relativePath告诉Maven相对于当前项目在哪里寻找项目。

    清单7. BigEngine Maven父配置
    <parent> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Shared</artifactId> <version>1</version> <relativePath>../Shared</relativePath> </parent>

    dependencies部分定义了该项目使用的所有项目。 每个依赖项都由一个groupId和一个artifactID 。 如果未指定特定依赖项的版本,则在上述父级中定义该依赖项的版本。

    清单8. BigEngine Maven依赖项配置
    <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-joda</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-mrbean</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-jaxb-annotations</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.timgroup</groupId> <artifactId>java-statsd-client</artifactId> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Persistence</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Engine</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Forecast</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>PersistenceLoader</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>PlayerDictionary</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Social</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.1.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>ScheduleOfPlay</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework</groupId> <artifactId>jersey-test-framework-core</artifactId> <version>2.16</version> <scope>test</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-inmemory</artifactId> <version>2.16</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.171</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>UnitTest</artifactId> <version>2.0.0-SNAPSHOT</version> <scope>test</scope> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>Charting</artifactId> <version>2.0.0-SNAPSHOT</version> <scope>test</scope> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-easymock</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.openjpa</groupId> <artifactId>openjpa</artifactId> </dependency> <dependency> <groupId>org.apache.wink</groupId> <artifactId>wink-guice-server</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>com.ibm.ei.bigdata</groupId> <artifactId>WebCrawler</artifactId> <version>2.0.4-SNAPSHOT</version> </dependency> </dependencies>

    单元和集成测试

    测试是软件开发实践的重要组成部分。 在PCC项目中实施了单元测试和集成测试。 单元测试是一种快速测试,可验证一小部分功能。 集成测试可能会比较慢,并且可以验证系统组件之间的交互。 JUnit用于单元和集成测试。

    Apache Maven构建系统规定了项目中测试的位置和布局。 单元测试位于项目的src / test / java子目录中。 集成测试位于项目的src / integration-test / java子目录中。 测试期间要使用的资源放置在test或test-integration目录的resources子目录中。 资源是单元测试和集成测试可以使用的非源文件。 例如,资源可能包含xml配置数据。 按照约定,单元测试文件名以Test.java结尾,集成测试以IT开始,以Test.java结尾。

    图3.单元和集成测试文件的布局

    要执行单元测试,请使用命令行mvn test调用Maven。 要执行集成测试,请使用命令行mvn integration-test 。 这将执行直到Maven构建过程的测试和集成测试阶段的所有阶段。

    对于PCC,我们使用JUnit框架进行单元测试。 JUnit测试用例像常规Java代码一样执行,并应用了一组断言来验证被测代码是否正常运行。 此外,我们利用Hamcrest匹配器来实现易于阅读的测试。 清单9显示了PCC使用的一种此类单元测试类。 在此单元测试中,将使用播放器信息填充数据存储,并解析XML文件,并将其内容放入对象中。 接下来,系统测试以确定是否可以在XML文件中的内容和数据存储之间完成交叉查找。

    清单9.示例单元测试
    package com.ibm.ei.engine.golf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import java.net.URL; import javax.persistence.EntityManager; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.junit.Test; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.ibm.ei.persistence.Player; import com.ibm.ei.persistence.Site; import com.ibm.ei.persistence.jpaloaders.Sites; import com.ibm.ei.zepplin.H2DatabaseTestCase; public class XMLFeaturedGroupsTest extends H2DatabaseTestCase { private final Function<Player, String> playerIDFunction = new Function<Player, String>() { @Override public String apply(Player input) { return input.getPlayerId(); } }; @Test public void testFromInputSupplier() throws Exception { EntityManager manager = getEntityManager(); Site site = Sites.loadSite(manager, "masters", 2013); URL group = Resources.getResource("featuredGroup.xml"); ImmutableList<FeaturedGroup> groups = XMLFeaturedGroups .fromInputSupplier(manager, site, Resources.asByteSource(group)); FeaturedGroup group1 = groups.get(0); FeaturedGroup group2 = groups.get(1); assertThat(group1, notNullValue()); assertThat(group2, notNullValue()); assertThat(group1.getPlayers(), notNullValue()); assertThat(group2.getPlayers(), notNullValue()); assertThat(group1.getPlayers().size(), is(3)); assertThat(group2.getPlayers().size(), is(3)); assertThat(Lists.transform(group1.getPlayers(), playerIDFunction), containsInAnyOrder("20396", "1810", "10885")); assertThat(Lists.transform(group2.getPlayers(), playerIDFunction), containsInAnyOrder("24357", "8793", "20229")); } @Override protected Optional<IDataSet> getDataSet() throws DataSetException { return Optional.of((IDataSet)new FlatXmlDataSetBuilder().build(Resources.getResource("players.xml"))); } }

    单个原子测试具有很多复杂性。 对段的检查可以深入了解PCC使用的测试方法。 包名称和导入在测试文件的顶部定义。 包名称很重要,应该映射您正在测试的类的包。 这使得程序包,私有或默认方法可在必要时进行测试。 导入定义了测试功能所需的所有类,并且它们本身被Java名称空间分为几部分。 Hamcrest静态导入用于使测试更易于阅读。 Dbunit导入用于协助持久性测试。 google.common库用于启用Java中的功能编程。 com.ibm类是带有某些测试帮助程序类的被测试类。

    清单10.示例单元测试的顶部
    package com.ibm.ei.engine.golf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import java.net.URL; import javax.persistence.EntityManager; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.junit.Test; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.ibm.ei.persistence.Player; import com.ibm.ei.persistence.Site; import com.ibm.ei.persistence.jpaloaders.Sites; import com.ibm.ei.zepplin.H2DatabaseTestCase;

    下一节定义Test类。 请注意,它以Test结束,这是惯例。 此特定测试扩展了H2DatabaseTest案例,该案例提供了加载持久性测试并允许在PCC中访问这些测试的通用方法。 playerIDFunction是一种功能编程方法,用于将集合从一种格式转换为另一种格式。 这在Test类的不同部分中使用。

    清单11.示例单元测试的顶部
    public class XMLFeaturedGroupsTest extends H2DatabaseTestCase { private final Function<Player, String> playerIDFunction = new Function<Player, String>() { @Override public String apply(Player input) { return input.getPlayerId(); } };

    下一部分定义了要在单元测试期间执行的测试。 @Test注释向JUnit测试框架发出信号,表明该方法应作为测试执行。 然后,该方法从父H2DatabaseTest案例类请求EntityManager 。 该类提供对持久性方法的访问。 然后,该方法使用Google Guava的Resources类来加载XML。 一旦完成,该方法就可以执行测试了。 首先,测试检查从XML文件加载的组均不为空。 接下来,执行相同的检查以验证每个组中是否包含玩家,并且每个已加载组中的玩家数量为3。验证之后,执行复合测试。 使用上述功能将组1和组2玩家的列表进行转换,以将玩家对象转换为每个玩家的玩家ID。 完成之后,单元测试框架将验证玩家ID列表是否等于预期的ID。 containsInAnyOrder方法用于不要求播放器采用任何特定顺序。

    清单12.测试代码
    @Test public void testFromInputSupplier() throws Exception { EntityManager manager = getEntityManager(); Site site = Sites.loadSite(manager, "masters", 2013); URL group = Resources.getResource("featuredGroup.xml"); ImmutableList<FeaturedGroup> groups = XMLFeaturedGroups .fromInputSupplier(manager, site, Resources.asByteSource(group)); FeaturedGroup group1 = groups.get(0); FeaturedGroup group2 = groups.get(1); assertThat(group1, notNullValue()); assertThat(group2, notNullValue()); assertThat(group1.getPlayers(), notNullValue()); assertThat(group2.getPlayers(), notNullValue()); assertThat(group1.getPlayers().size(), is(3)); assertThat(group2.getPlayers().size(), is(3)); assertThat(Lists.transform(group1.getPlayers(), playerIDFunction), containsInAnyOrder("20396", "1810", "10885")); assertThat(Lists.transform(group2.getPlayers(), playerIDFunction), containsInAnyOrder("24357", "8793", "20229")); }

    父测试类使用清单13中描述的下一部分,将数据加载到数据存储中,以供持久性框架访问。 该代码指定一个XML文件,该文件是数据库表的转储。 然后将该表加载到内存数据库中,以进行快速的单元测试。

    清单13.数据集加载
    @Override protected Optional<IDataSet> getDataSet() throws DataSetException { return Optional.of((IDataSet)new FlatXmlDataSetBuilder().build(Resources.getResource("players.xml"))); }

    集成测试的执行与单元测试一样。 在PCC中,集成测试可能会运行更长的时间,并且可能会测试多个组件之间的集成。

    通常,在测试复杂的系统时,需要模拟无法轻松测试的组件。 此类组件可能包括诸如网络访问,文件访问之类的内容,或一些在测试环境中不容易重新创建的其他资源。 模拟是将接口连接到那些组件并定义该接口将响应的模式的过程。 然后将模拟的界面或对象呈现给被测对象。 测试可以继续进行,而不需要测试期间可能不可用的资源。 预测云计算使用EasyMock框架进行测试模拟。 下面的代码部分演示了一种用于检查高尔夫比赛的配对的测试模型。 被测对象需要一个复杂的站点对象,该对象通常需要访问数据库。 为了便于测试,该对象将被模拟。 模拟设置包括使用要模拟的特定类创建新的模拟,然后列出预期的方法调用以及调用时返回的内容。 然后将此对象传递到调度分析器,该分析器无需数据库访问就可以测试该模块。

    清单14.模拟
    @Test public void testParseFile() throws Exception { final File file = new File("src/test/resources/mastersreplay/2014/pairings1.json"); final FileReader reader = new FileReader(file); final Reader fixedReader = PairingFileParser.fixJSONFormatting(reader); final DateTime nowMidnight = DateTime.now().withMillisOfDay(0); final Site site = EasyMock.createMock(Site.class); expect(site.getName()).andReturn("masters"); expect(site.getYear()).andReturn(2013); expect(site.getTimezoneOffset()).andStubReturn(0); expect(site.getTournamentScheduleStart()).andReturn(nowMidnight).anyTimes(); replay(site); final ImmutableList<PlayerTeeTime> result = PairingFileParser.parseSchedule(fixedReader, site); assertEquals(96, result.size()); final DateTime expectedTime = nowMidnight.withZone(DateTimeZone.forOffsetHours(-4)).withHourOfDay(7).withMinuteOfHour(50); assertEquals(expectedTime, result.get(0).getTeeTime()); assertEquals(expectedTime.getMillis(), result.get(0).getTeeTime().getMillis()); assertEquals(expectedTime.getMillis(), result.get(1).getTeeTime().getMillis()); assertEquals(expectedTime.getMillis(), result.get(2).getTeeTime().getMillis()); }

    静态分析测试

    静态分析测试与单元测试和集成测试不同,因为它不执行。 相反,将检查源代码以查找反模式和其他类型的错误。 静态分析测试有助于维护代码库并验证所交付的代码是否符合团队或行业标准。

    PCC项目利用SonarQube进行静态分析测试。 选择SonarQube是因为其出色的图形界面以及能够帮助团队确定需要改进代码的领域的能力。

    SonarQube的用户界面是高度可配置的。 PCC将SonarQube的主页配置为包含三个部分。 左上方的部分显示了一个时间表,其中包含代码行数,测试覆盖率和未解决的问题。 右侧显示了PCC的所有项目,并带有一些简单的统计数据,例如代码行和技术债务。 技术债务以修复通过静态分析检测到的所有缺陷所需的大约时间量来衡量。 左下方描绘了一个热图,其中每个正方形的大小代表代码行,而色调则显示测试覆盖范围的行。 开发人员可以借此可视化哪些项目需要最大的改进。

    图4. SonarQube用户界面

    如果从图5的右侧选择了一个项目,则将为用户提供该特定项目的静态分析的概述。 PCC配置为显示有关所选项目的以下详细信息:债务,重复,一段时间内的复杂度,当前复杂度,一段时间内的大小,代码行和单元测试详细信息。 PCC团队选择了项目页面的每个部分,以提供对项目状态的有意义的洞察。

    图5. SonarQube项目界面

    用户界面的技术债务部分提供了解决项目中所有已知问题所需的时间以及问题的总数和每个问题的严重性的高级摘要。 红色问题(最上面的三个)的严重性高于绿色(下面两个)的严重性。

    图6. SonarQube债务界面

    代码重复会导致代码难以维护。 通常,如果代码重复,则应将其提取到方法中。 如果在整个代码库中复制和粘贴代码,则可能很难维护,因为一个位置的代码更改很容易在另一位置被忘记。 这会导致缺陷。 SonarQube提供了一个视图,使您可以快速查看项目中重复了多少代码。 视觉效果提供了有关重复项的多个指标:百分比,行数,块数和包含重复项的文件数。

    图7. SonarQube复制界面

    复杂的代码很难理解。 通常,直到另一名开发人员必须维护该代码并理解其功能之后,才能清楚一段代码的复杂程度。 注释可能会有所帮助,但是静态分析可以帮助衡量代码的复杂性并确定代码中哪些方面需要改进。 为PCC配置的代码重复检测指示每个类,文件和功能的复杂性。 在一个有很多贡献者的项目中,尝试随着时间的推移降低复杂性指标是值得的。

    图8. SonarQube代码复杂度

    就像复杂性一样,如果单个项目包含太多代码,则可能很难理解。 当项目规模开始扩大时,开发人员应考虑将其重构为多个项目。 此外,检测对于了解测试覆盖了多少百分比的代码行很有用。 在PCC中,SonarQube配置为显示代码行,类数以及这些类的测试覆盖率。

    图9. SonarQube代码大小

    随时间变化的代码大小视图可帮助开发人员发现代码趋势。 SonarQube提供了另一个视图,可让您更深入地了解项目的当前代码大小指标。 配置了代码大小详细信息视图,以显示项目中的代码总数,文件,目录,函数,类,语句和访问器的数量。 此信息可以向开发人员指示一个项目是否太大而无法维护,应该分解为多个项目。

    图10. SonarQube代码大小细节

    如前所述,测试是开发软件非常重要的实践,PCC也不例外。 SonarQube为项目的当前测试状态提供了良好的可视化见解。 开发人员可以快速查看具有单元测试覆盖率的代码百分比以及具有覆盖率的代码条件百分比。 开发人员还可以通过失败计数,错误列表和总测试时间等指标轻松查看测试的成功程度。

    图11. SonarQube测试的覆盖范围和性能

    结论

    构建自动化,单元和集成测试以及静态分析的结合提高了PCC的可靠性,同时减少了在生产中调试代码所需的时间。 在开始项目时,可以很容易地忽略测试和构建自动化,但是在开始时就具有它们很重要。 如果项目在开始时缺乏良好的测试卫生,那么在项目后期引入它可能会因技术债务而变得不知所措。 使用构建和测试自动化来启动项目可能会导致代码更具可维护性。

    在本系列的第7部分中,我们将介绍整体PCC体系结构中IBM®DB2®的实现和时间序列数据库Graphite。


    翻译自: https://www.ibm.com/developerworks/analytics/library/ba-bluemix-predictive-cloud-computing-big-data-storage-analytics-trs/index.html

    相关资源:微信小程序源码-合集6.rar
    Processed: 0.016, SQL: 9