Maven Basics: Difference between revisions

From Okapi Framework
Jump to navigation Jump to search
No edit summary
 
m (1 revision imported)
 
(No difference)

Latest revision as of 19:26, 4 June 2016

Maven is the build and dependency management tool used for the Okapi Framework. There is plenty of documentation scattered across the web. This page will simply provide links to the most pertinent documentation and highlight basic operations.

Basic Project Design

The recommended maven project structure is to have a parent-child pom relationship. There are pom type projects, jar type projects, war type projects, ear type project, etc...

The POM projects do not package any code and are used mostly for common configuration and organization/hierarchy of modules.

Okapi will have a pom in the project root which all of the other modules should inherit from (this allows each module to share similar configurations with out explicitly declaring them in each project pom - code coverage, site, junit, common OSGi config, etc...)

Additional information on Parent-child relationships can be found here and on Project Inheritance can be found here.

Okapi uses a hybrid approach of using both a multi-module and inheritance model.

A basic maven project is structured according to set guidelines. Tests for a project are included in the same module. Item in the resources directory are included in the classpath or artifact (test->resources are only included while running tests).

Project structure:

pom.xml   
META-INF/MANIFEST.MF
src
   main
      java
      resources
   test
      java
      resources

Here is an example of a pom file (of type POM) that contains the pointers to the modules and produces no artifacts:

<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/maven-v4_0_0.xsd">
   <parent>
      <artifactId>build-okapi</artifactId>
      <groupId>net.sf.okapi</groupId>
      <version>1.0.0</version>
   </parent>
   <artifactId>build-common</artifactId>
   <packaging>pom</packaging>
   <modelVersion>4.0.0</modelVersion>
   <name>Okapi Common Build</name>
   <modules>
       <module>common</module>
       <module>filters</module>
       <module>pipeline</module>
       <module>ui</module>
       <module>ui.filters</module>
   </modules>
</project>

Here is an example of a POM using the above parent POM:

<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/maven-v4_0_0.xsd">
   <parent>
      <artifactId>build-common</artifactId>
      <groupId>net.sf.okapi</groupId>
      <version>1.0.0</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>common.pipeline</artifactId>
   <name>Okapi Pipeline</name>
   <dependencies>
      <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>common.filters</artifactId>
        <version>${project.version}</version>
      </dependency>
   </dependencies>
   <build>     
     <plugins>
         <plugin>
           <artifactId>maven-jar-plugin</artifactId>
           <configuration>
              <!--All JAR projects will have this same configuration -->
              <!-- This projects generated JAR will look like: net.sf.okapi.common.pipeline-1.0.0.jar -->
              <finalName>${pom.groupId}.${pom.artifactId}-${pom.version}</finalName>
           </configuration>
         </plugin>
     </plugins>
  </build>
</project>

Dependencies

A maven module is identified by group, artifact and version id. The unique combination of these represent a module/project and provides the structure for storing and retrieval of artifacts and dependencies from local and remote repositories.

See also: http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

Testing

Test Modules

It is possible to have a module that is used exclusively for shared test utilities. Configuring this project to be a test jar is as simple as adding the following to to your pom:

<build>
   <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
             <goals>
              <goal>test-jar</goal>
             </goals>
           </execution>
         </executions>
       </plugin>
   </plugins>
</build>

The above configuration assumes there is code in the src/test/java directory which will then be used by other modules. It is ok to have releasable code in the src/main/java directory but this code will not be included in the test jar. To use this as a dependency of tests from another project, simply add the following:

<dependency>
   <groupId>the.group.id</groupId>
   <artifactId>the-artifact</artifactId>
   <scope>test</scope>
   <classifier>tests</classifier>
   <version>the-version-number</version>
</dependency>

Unit Tests

If code can be related to a tree, then unit tests are generally written against the leaf nodes of that tree. However, if unit tests were only written against the leaf nodes, then that would mean a lot of code isn't getting tested (or covered). When testing non-leaf node methods, then it is generally a good idea to stub or mock out portions of the object graph such that you can focus on testing the code in the given method.

Unit tests should be extremely fast for the following reasons:

  • They should be ran at the very least before every commit (I run them between every build or change in code)
  • There are usually more unit tests than any other type of test as it is easier to write unit tests (once the skill is acquired) for the different scenarios as there is no need to worry about the big picture. One example of this might be to test the password validation that occurs when a user changes his password or when a new user is created. One way to test this would be to write an integration test that creates the necessary object graph, runs a method that ends up calling the code to check the password validation. This can get cumbersome when trying multiple scenarios. However, writing a unit test would likely simply require passing a string into the validatePassword method. Not only is this test much simpler to test different file extensions, but it will also run much faster.
  • As a general rule of thumb, all automation should run as fast possible as automated tests begin to accumulate and once they start taking a while to run, we will all be tempted not to run them.

Generally, it is frowned on for a test to require access to something outside of the code base (database, file system ...). The reason behind this is to keep the unit tests as fast as possible. Personally, I like to be able to run all unit tests within one, maybe two, minutes.

All unit tests for a given module should be self-contained inside the module itself. The src/test/java directory is the place where these tests should go. This has several advantages. One of which is that the same package can be used, making testing protected or default methods much easier. In other words, please do not add a "tests" to the package structure.

Integration Tests

For now, it is recommended that we place all integration tests in a separate module and that module should not contain any code outside of the tests themselves. There should be one integration tests module for every major directory. For example, the steps directory would have a single integration tests module.

The project structure for an integration test module would look something like the following:

okapi
   steps
      integration-tests
         src
            test
               java
               resources

Notice how there are no src/main/java directories and therefore no shippable code for that module.

Telling Maven to Download Sources and JavaDoc

mvn dependency:sources
mvn dependency:resolve -Dclassifier=javadoc

Basic Maven Commands

mvn clean - Removes compiled code, resources and artifacts from target directory.

mvn site - Generates the html site for the project - reports, documentation, etc.

mvn compile - Compiles the project source code.

mvn package - Bundles the compiled source code and resources into desired packaging (jar, war, etc..)

mvn install - Installs the packaged artifact into the local repository - essential for integration tests.

mvn test - Executes unit tests for project(s).

mvn verify -P integration - Executes integration and unit tests for project(s).

mvn dependency:resolve - Shows a flat list of what will be included in the class path (including transitive dependencies).

mvn dependency:tree - Shows a hierarchical list of what will be included in the class path (including transitive dependencies).

mvn dependency:sources - Downloads the source code for all libraries that have one.