Thursday, February 27, 2014

Objectify 4 TDD Maven Hello World Tutorial with Google App Engine

I've just started getting my hands dirty with Objectify (link) with the Google App Engine (link) and finally started small with unit tests instead of writing a bunch of code that breaks due to 3rd party modules not doing what the documentation says they do, i.e. using Test Driven Development (TDD) in practice.  I came across a fair amount of out-dated tutorials (link link), old documentation (link), forum posts (link) and Stack Overflow questions so it's a perfect candidate for a tutorial post.  In this tutorial we are going to write a JUnit test for objectify-appengine with a persisted Car object with an out-of-place VIN of "Hello World".

First off, figuring out those nasty Maven dependencies was quite a task by itself.  The objectify docs will say you only need objectify (link) but you need the full compliment of Google App Engine dependencies (link link) as well as a Java Persistence API (JPA) implementation (we use Apache Open JPA here, link).

<dependencies>

  <!-- Objectify -->
  <dependency>
    <groupid>com.googlecode.objectify</groupid>
    <artifactid>objectify</artifactid>
    <version>4.0b3</version>
  </dependency>

  <!-- Open JPA -->
  <dependency>
    <groupid>org.apache.openjpa</groupid>
    <artifactid>openjpa-all</artifactid>
    <version>2.2.2</version>
  </dependency>
  

  <!-- GAE Dependencies -->

  <dependency>
    <groupid>com.google.appengine</groupid>
    <artifactid>appengine-api-1.0-sdk</artifactid>
    <version>${appengine.target.version}</version>
  </dependency>
  
  <dependency>
    <groupid>com.google.appengine</groupid>
    <artifactid>appengine-testing</artifactid>
    <version>${appengine.target.version}</version>
    <scope>test</scope>
  </dependency>
  
  <dependency>
    <groupid>com.google.appengine</groupid>
    <artifactid>appengine-api-labs</artifactid>
    <version>${appengine.target.version}</version>
    <scope>test</scope>
  </dependency>
  
  <dependency>
    <groupid>com.google.appengine</groupid>
    <artifactid>appengine-api-stubs</artifactid>
    <version>${appengine.target.version}</version>
    <scope>test</scope>
  </dependency>
</dependencies>

For the Plain Old Java Object (POJO) to persist we are going with the Car class as in the Objectify documentation (link), except they forgot about the @Entity annotation (link).

package ...;

// imports included for your convenience
import javax.persistence.Transient;

import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;

@Entity
public class Car {
  @Id
  Long id;

  String vin;

  int color;

  @Transient
  String doNotPersist;

  static {
    ObjectifyService.register(Car.class);
  }

  private Car() {
  }

  public Car(String vin, int color) {
    this.vin = vin;
    this.color = color;
  }

}

Lastly we have the test class itself.  Note that save() and load() is used in Objectify 4 API (link) instead of put(...) and get(...) in the old API.  These methods also support using Keys (link) but I wasn't able to figure out how you got Keys at that time, so the example (modified from the Objectify documentation) uses IDs.

package ...;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

public class ObjectifyAdapterTest {

  private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
      new LocalDatastoreServiceTestConfig());

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
  }

  @AfterClass
  public static void tearDownAfterClass() throws Exception {
  }

  @Before
  public void setUp() throws Exception {
    helper.setUp();
  }

  @After
  public void tearDown() throws Exception {
    helper.tearDown();
  }

  @Test
  public void test() {
    final Objectify ofy = ObjectifyService.ofy();

    // Simple create, note the cheesy use of Hello World
    final Car porsche = new Car("Hello World", 3);
    ofy.save().entities(porsche).now();
    final Long id = porsche.id;
    assert id != null; // id was autogenerated

    // Get it back
    final Car loadedPorsche = ofy.load().type(Car.class).id(id).now();

    assert porsche.equals(loadedPorsche);

    // Change some data and write it
    porsche.color = 1;
    ofy.save().entities(porsche).now();

    // Delete it
    ofy.delete().entity(porsche).now();
  }
}

Some key points are the helper object standing in for the Google App Engine persistence mechanism and that you get the same object back that you saved.  Enjoy your quick start with Objectify, TDD, Maven and the Google App Engine!

3 comments:

  1. hi,
    its a good post. If you never mind can you please give me the list of jars required(or pom.xml) for the above example. I need a basic example for objectify from end to end.
    It would be helpful if you give me the source code of this example also.
    Thank you

    ReplyDelete
  2. [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ Objectifys
    ample-war ---
    [INFO] skip non existing resourceDirectory D:\Googleappexample\Objectifysample\O
    bjectifysample-war\src\main\resources
    [INFO]
    [INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ Objectifysamp
    le-war ---
    [INFO] Compiling 2 source files to D:\Googleappexample\Objectifysample\Objectify
    sample-war\target\Objectifysample-war-1.0-SNAPSHOT\WEB-INF\classes
    [INFO] -------------------------------------------------------------
    [ERROR] COMPILATION ERROR :
    [INFO] -------------------------------------------------------------
    [ERROR] D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\co
    m\objectify\sample\ObjectifyAdapterTest.java:[10,53] error: package com.google.a
    ppengine.tools.development.testing does not exist
    [ERROR] D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\co
    m\objectify\sample\ObjectifyAdapterTest.java:[11,53] error: package com.google.a
    ppengine.tools.development.testing does not exist
    [ERROR] D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\co
    m\objectify\sample\ObjectifyAdapterTest.java:[17,16] error: cannot find symbol
    [ERROR] symbol: class LocalServiceTestHelper
    location: class ObjectifyAdapterTest
    D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\com\object
    ify\sample\ObjectifyAdapterTest.java:[10,53] error: package com.google.appengine
    .tools.development.testing does not exist
    [ERROR] D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\co
    m\objectify\sample\ObjectifyAdapterTest.java:[11,53] error: package com.google.a
    ppengine.tools.development.testing does not exist
    [ERROR] D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\co
    m\objectify\sample\ObjectifyAdapterTest.java:[17,16] error: cannot find symbol
    [ERROR] symbol: class LocalServiceTestHelper
    location: class ObjectifyAdapterTest
    D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\com\object
    ify\sample\ObjectifyAdapterTest.java:[17,52] error: cannot find symbol
    [ERROR] symbol: class LocalServiceTestHelper
    location: class ObjectifyAdapterTest
    D:\Googleappexample\Objectifysample\Objectifysample-war\src\main\java\com\object
    ify\sample\ObjectifyAdapterTest.java:[18,10] error: cannot find symbol





    com.googlecode.objectifyobjectify5.0.2
    org.apache.openjpaopenjpa-all2.2.2
    com.google.appengineappengine-api-1.0-sdkversion>${appengine.target.version}



    com.google.appengine
    appengine-testing
    ${appengine.target.version}
    test



    com.google.appengine
    appengine-api-labs
    ${appengine.target.version}
    test



    com.google.appengine
    appengine-api-stubs
    ${appengine.target.version}
    test



    junit
    junit
    4.8.1

    ReplyDelete
  3. Here, let me not notice this comment for two years :(
    For the record it looks like your IDE is not finding the test jars, including the appengine-testing maven dependency because your class is in src/main/java instead of in src/test/java. Either move your code into src/test/java or remove the test scope from the test dependencies so that they are visible during the (non-test) compile phase. The first option is considered better practice.

    ReplyDelete