Problem:
I am adding tests and refactoring existing Spring #RestControllers.
The urls used to be hard coded in the Rest annotations.
#GetMapping("/api/v1/{taxonomy}/animals")
public List<String> getAnimals() {
...
}
Naming scheme:
I started moving them into constants. So the tests will be easier maintainable and not break on trivial url changes.
I came up with my own naming scheme for the url constants. (see the code at the end of the question for my complete scheme)
public static final String SERVICE_URL = "/api/v1/{taxonomy}/animals";
public static final String PATH_VAR_ANIMAL = "animal";
Are there any best practices for this? I was unable to find somethings and all the examples I found use hard coded strings.
Url construction:
Also a co-worker pointed out that string concatenation for url construction is risky. e.g. somebody could forget a '/' at the start of one url part.
#PostMapping(URL_PART_ANIMAL + URL_PART_FUR)
Is there a preferred way to build this kind of urls as they are not complete but only the end of an actual url?
Complete example:
package com.huelfe.animal.api.v1.rest;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(AnimalController.SERVICE_URL)
public class AnimalController {
public static final String SERVICE_URL = "/api/v1/{taxonomy}/animals";
public static final String PATH_VAR_ANIMAL = "animal";
public static final String URL_PART_ANIMAL = "/{" + PATH_VAR_ANIMAL + "}";
public static final String URL_PART_RELATIVE = "/relative";
public static final String URL_PART_RENAME = "/rename";
public static final String URL_PART_FUR = "/fur";
public static final String PARAM_NAME = "name";
public static final String PARAM_FUR = "fur";
#GetMapping
public List<String> getAnimals() {
...
}
#GetMapping(value = URL_PART_RELATIVE)
public Map<String, Collection<String>> getAnimalsRelatives() {
...
}
#PostMapping
public Result doAdd(#RequestParam(value = PARAM_NAME) #NotBlank String newAnimal) {
...
}
#DeleteMapping(value = URL_PART_ANIMAL)
public Result doRemove(#PathVariable(PATH_VAR_ANIMAL) String animal) {
...
}
#PutMapping(URL_PART_ANIMAL + URL_PART_RENAME)
public Result doRename(
#PathVariable(PATH_VAR_ANIMAL) String animal,
#RequestParam(value = PARAM_NAME) #NotBlank String newName) {
...
}
#PostMapping(URL_PART_ANIMAL + URL_PART_FUR)
public Result addFurs(
#PathVariable(PATH_VAR_ANIMAL) String targetAnimal,
#RequestParam(value = PARAM_FUR) #NotEmpty List<#NotBlank String> furs) {
...
}
}
Related
I recently work with Commercetools platform and I have such a question. How we can find product or category and so on by incomplete name?
For example if in my url I wrote something like this
https://localhost:8080/cat?catName=G
I wanna find all categories which contains a G . How we can done this?
You can get this with a categoryAutoComplete query on the GraphQL API. The following query asks for categories beginning with "hi". You need to provide two characters at least, with one letter only it doesn't return any result.
{
categoryAutocomplete(locale: "en", text: "hi") {
results {
name(locale: "en")
}
}
}
On my test project it this query returns two categories that have the term "hint" in their English name:
{
"data": {
"categoryAutocomplete": {
"results": [
{
"name": "Test duplicate order hint"
},
{
"name": "order hint test"
}
]
}
}
}
Is this helpful?
You can make a GraphQL request with the commercetools JVM SDK as follows:
First implement the Java class representing your GraphQL response. So if one result object looks like:
{
"name": "category name"
}
Then the implementing java class CategoryAutoCompleteResult would look something like this:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.sphere.sdk.models.Base;
public class CategoryAutoCompleteResult extends Base {
private final String name;
#JsonCreator
public CategoryAutoCompleteResult(#JsonProperty("name") final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Then implement the GraphQL request class CategoryAutoCompleteRequest. This could be simplified by just implementing the SphereRequest interface from the commercetools JVM SDK as follows:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import io.sphere.sdk.client.HttpRequestIntent;
import io.sphere.sdk.client.SphereRequest;
import io.sphere.sdk.http.HttpMethod;
import io.sphere.sdk.http.HttpResponse;
import io.sphere.sdk.json.SphereJsonUtils;
import io.sphere.sdk.models.Base;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
public class CategoryAutoCompleteRequest extends Base implements SphereRequest<List<CategoryAutoCompleteResult>> {
private final String queryString;
CategoryAutoCompleteRequest(final String queryString) {
this.queryString = queryString;
}
public static CategoryAutoCompleteRequest of(#Nonnull final String queryString) {
return new CategoryAutoCompleteRequest(queryString);
}
#Nullable
#Override
public List<CategoryAutoCompleteResult> deserialize(final HttpResponse httpResponse) {
final JsonNode rootJsonNode = SphereJsonUtils.parse(httpResponse.getResponseBody());
final JsonNode results = rootJsonNode.get("data").get("categoryAutocomplete").get("results");
return SphereJsonUtils
.readObject(results, new TypeReference<List<CategoryAutoCompleteResult>>() {
});
}
#Override
public HttpRequestIntent httpRequestIntent() {
final String queryBody =
String.format("{categoryAutocomplete(locale: \"en\", text: \"%s\") {results{name(locale: \"en\")}}}",
queryString);
final String graphQlQuery = buildGraphQlQuery(queryBody);
return HttpRequestIntent.of(HttpMethod.POST, "/graphql", graphQlQuery);
}
private static String buildGraphQlQuery(#Nonnull final String queryBody) {
return String.format("{ \"query\": \"%s\"}", queryBody.replace("\"", "\\\""));
}
}
Then the last step would be to execute the actual request. Assuming you already have a setup SphereClient. Executing the request would looks as follows:
final List<CategoryAutoCompleteResult> results = sphereClient
.execute(CategoryAutoCompleteRequest.of("myIncompleteCatName")).toCompletableFuture().join();
I am coding in spring-boot. I tried to get the value of properties.properties in other packages without success. For example in the classe ClassUtils.java, the value of testValue is always null
This is my project
This is my code:
package com.plugins.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.plugins.domain.ClassUtils;
#RestController
public class SearchContactController {
#Value("${environnement.test}")
String testValue;
#Value("${environnement.url}")
String urlValue;
#RequestMapping(value = "/test")
public String pingRequest() {
System.out.println("value ===> " + testValue + " /// " + urlValue);
return "test !" + ClassUtils.getTestValue();
}
}
This is my second class, where I can't get the value of testValue variable:
package com.plugins.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class ClassUtils {
#Value("${environnement.test}")
static String testValue;
public static String getTestValue(){
return "The return "+testValue;
}
}
This is my springApp.java
package com.plugins;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootVideApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootVideApplication.class, args);
}
}
Enable #ComponentScan({"com.plugins"}) , in Application
To access the properties defined in application.properties
myapp.url="xxxxxx"
in your class
#Value("${myapp.url}")
private String testValue;
but this cannot be a static variable, if it is a static variable you do some hack like this, by defining setter method
private static String testValue;
#Value("${myapp.url}")
public void testValue(String value) {
testValue = value;
}
I resolve this issue by addin #Autowired in the class which use the method of the other class this is a snippet
// Class: SearchContactController.java
#Autowired
ClassUtils cd;
#RequestMapping(value = "/ping")
public String pingRequest() {
return "Ping OK !" + cd.getTestValue();
}
Currently I'm rendering a command object in a MessageBodyReader but I'd like to be able to do this in a #BeanParam:
Inject a field derived from the SecurityContext (Is there somewhere to hook in the conversion?).
have a field inject that has been materialised by a MessageBodyReader.
Is this possible ?
Note: Go Down to UPDATE. I guess it is possible to use #BeanParam. Though you need to inject the SecurityContext into the bean and extract the name info.
There's no way to achieve this with #BeanParam corrected. You could use a MessageBodyReader the way you are doing, but IMO that's more of a hack than anything. Instead, the way I would achieve it is to use the framework components the way they are supposed to be used, which involves custom parameter injection.
To achieve this, you need two things, a ValueFactoryProvider to provide parameter values, and a InjectionResolver with your own custom annotation. I won't do much explaining for the example below, but you can find a good explanation in
Jersey 2.x Custom Injection Annotation With Attributes
You can run the below example like any JUnit test. Everything is included into the one class. These are the dependencies I used.
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
And here is the test
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.security.Principal;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectionTest extends JerseyTest {
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public static #interface CustomParam {
}
public static class CustomModel {
public String name;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
public static class CustomParamValueFactory
extends AbstractContainerRequestValueFactory<CustomModel> {
#Override
public CustomModel provide() {
ContainerRequest request = getContainerRequest();
String name = request.getSecurityContext().getUserPrincipal().getName();
RequestBody body = request.readEntity(RequestBody.class);
CustomModel model = new CustomModel();
model.body = body;
model.name = name;
return model;
}
}
public static class CustomValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public CustomValueFactoryProvider(MultivaluedParameterExtractorProvider multiProvider,
ServiceLocator locator) {
super(multiProvider, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
if (CustomModel.class == parameter.getType()
&& parameter.isAnnotationPresent(CustomParam.class)) {
return new CustomParamValueFactory();
}
return null;
}
}
public static class CustomParamInjectionResolver extends ParamInjectionResolver<CustomParam> {
public CustomParamInjectionResolver() {
super(CustomValueFactoryProvider.class);
}
}
private static class CustomInjectBinder extends AbstractBinder {
#Override
protected void configure() {
bind(CustomValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(CustomParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomParam>>(){})
.in(Singleton.class);
}
}
private static final String PRINCIPAL_NAME = "peeskillet";
#PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
#Path("test")
public static class TestResource {
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String post(#CustomParam CustomModel model) {
return model.name + ":" + model.body.message;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class)
.register(new CustomInjectBinder());
}
#Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(PRINCIPAL_NAME + ":" + body.message, responseBody);
System.out.println(responseBody);
}
}
Note that I read the request body from the ContainerRequest inside the CustomParamValueFactory. It is the same RequestBody that I sent in JSON from the request in the #Test.
UPDATE
So to my surprise, it is possible to use #BeanParam. Here is the following bean I used to test
public static class CustomModel {
#Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
The difference from the previous test is that instead of the name from the SecurityContext.Principal, we need to inject the entire SecurityContext. There's just no way for the inject to get the name from the Principal, So we will just do it manually.
The thing that surprised me the most though, is that we are able to inject the RequestBody entity. I didn't know this was possible.
Here is the complete test
import java.io.IOException;
import java.security.Principal;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectTestTake2 extends JerseyTest {
private static final String PRINCIPAL_NAME = "peeskillet";
private static final String MESSAGE = "Hello World";
private static final String RESPONSE = PRINCIPAL_NAME + ":" + MESSAGE;
public static class CustomModel {
#Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
#PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
#Path("test")
public static class TestResource {
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String post(#BeanParam CustomModel model) {
return model.securityContext.getUserPrincipal().getName()
+ ":" + model.body.message;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class);
}
#Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(RESPONSE, responseBody);
System.out.println(responseBody);
}
}
See Also:
Custom Injection and Lifecycle Management
i create a mapper with
new ObjectMApper()
.setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE)
.setSerializationInclusion(Include.NON_NULL)
and serialization works perfectly on fields (no getters and setters). field currentStatus is serialized to "currentStatus" (first letter uppercase). but i have also one getter (without a field and setter) which must be camelCase. so i do:
#JsonProperty("abcDef")
public String getZxy() {...
but it is serialized to "AbcDef" instead of "abcDef". it looks like naming strategy still triggers and change the first letter. i use jackson-databind 2.3.2;
how can i map this getter with first letter lowercase?
EDIT:
ugly code, but shows the problem. this test should pass but it fails
import static org.assertj.core.api.Assertions.assertThat;
import org.testng.annotations.Test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
public class JsonFailureTest {
#Test
public void should_serialize_first_letter_lowercase() throws Exception {
String json = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE)
.writeValueAsString(
new Object(){
#JsonProperty("fooBar")
public String whatever() {return "";}
});
assertThat(json).contains("fooBar");
}
}
Here's a workaround using a custom "annotation-aware" strategy:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
public class Foo {
public static void main(final String[] args) throws JsonProcessingException {
final SomeObject someObject = new SomeObject();
someObject.setZxy("foobar");
final ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.PascalCaseStrategy() {
#Override
public String nameForGetterMethod(final MapperConfig<?> config, final AnnotatedMethod method, final String defaultName) {
final JsonProperty annotation = method.getAnnotation(JsonProperty.class);
if (annotation != null) {
return annotation.value();
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
System.out.println(mapper.writeValueAsString(someObject));
}
private static class SomeObject {
private String zxy;
#JsonProperty("abcDef")
public String getZxy() {
return this.zxy;
}
public void setZxy(final String zxy) {
this.zxy = zxy;
}
}
}
Output:
{"abcDef":"foobar"}
I want to do some automated datastore tests for the Google App Engine locally with Junit.
I have written a class 'Agent.java' with three Strings 'name', 'owner' and 'url'. The class 'Player' is abstract, but does not provide additional attributes.
public class Agent extends Player implements Serializable {
/** to serialize Agent */
private static final long serialVersionUID = -6859912740484191335L;
/** The name of the Agent is the key-element of the agent-class*/
#Id String name;
/** Url to the Agent */
String url;
#Index String owner;
...
Followed by Setters and Getters.
I have copied the 4 needed library from the sdk 1.6.0 to the projects 'war/WEB-INF/lib' folder and included the Junit4 Container.
My test class looks like this:
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Logger;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.code.twig.annotation.AnnotationObjectDatastore;
public class AgentContrTest {
private static final Logger log = Logger.getLogger("AgentContrTest.class");
private static UserController uc;
private static GameController gc;
private static AgentController ac;
private static final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
private AnnotationObjectDatastore datastore = new AnnotationObjectDatastore(false);
#BeforeClass
public static void setUpOnce() {
gc = GameController.getInstance();
uc = UserController.getInstance();
ac = AgentController.getInstance();
}
#Before
public void setUp() {
helper.setUp();
try {
uc.register("userForTest", "test", "test#gmail.de", false);
}
catch (NameExistsException ne) {
}
catch (EmailFormatException ee) {
}
}
#After
public void tearDown() {
helper.tearDown();
}
// Testing the raising of NameExistsException in createAgent(String name, String url, String owner)
#Test(expected=NameExistsException.class)
public void testCreateAgentExc1() throws NameExistsException {
Agent ag1 = ac.createAgent("Agent1", "www.agent1.com", "Owner1");
Agent ag2 = ac.createAgent("Agent1", "www.agent2.com", "Owner2");
}
// Testing getAgents()
#Test
public void testGetAgents1() throws NameExistsException {
datastore.disassociateAll();
ArrayList<Agent> agents1 = ac.getAgents();
ac.createAgent("Agent1", "www.agent1.com", "Owner1");
ac.createAgent("Agent2", "www.agent2.com", "Owner2");
ac.createAgent("Agent3", "www.agent3.com", "userForTest");
ArrayList<Agent> agents2 = ac.getAgents();
assertTrue(agents1.size()==0);
assertTrue(agents2.size()==3);
datastore.disassociateAll();
}
// Testing getAgents(String user)
#Test
public void testGetAgents2() throws NameExistsException {
ArrayList<Agent> agents = ac.getAgents();
assertTrue(agents.size()==0);
datastore.disassociateAll();
ac.createAgent("Agent1", "www.agent1.com", "Owner1");
ac.createAgent("Agent2", "www.agent2.com", "Owner2");
ac.createAgent("Agent3", "www.agent3.com", "userForTest");
ArrayList<Agent> agents2 = ac.getAgents("userForTest");
assertTrue(agents2.size()==1);
}
These are the functions in my AgentController that I am testing:
public ArrayList<Agent> getAgents(String user) {
ArrayList<Agent> agents = new ArrayList<Agent>();
Iterator<Agent> agentIterator = datastore.find().type(Agent.class)
.addFilter("owner", FilterOperator.EQUAL, user)
.now();
while (agentIterator.hasNext()) {
agents.add(agentIterator.next());
}
return agents;
}
public Agent createAgent(String name, String url, String owner) throws NameExistsException {
Agent agent = datastore.load(Agent.class, name);
if (agent != null)
throw new NameExistsException();
agent = new Agent();
agent.setName(name);
agent.setUrl(url);
agent.setOwner(owner);
datastore.store(agent);
return agent;
}
The testCreateAgentExc1 is working just fine. But the testGetAgents2() is throwing a NameExistsException, which it should not do. If i rename the agents in this test to 'Agent4' to 'Agent6' it is working just fine.
Due to 'http://code.google.com/intl/de-DE/appengine/docs/java/tools/localunittesting.html'
the Datastore should delete all data between the tests, so the NameExistsException should not be raised.
You are not resetting your datastore object between tests. I'm not sure how twig works, but it (or its configuration) is the cause of the leak.
I don't have experience in testing GAE, but there's one difference between your code and the code on Google's page.
You're using a class variable
private static final LocalServiceTestHelper helper
= new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
whereas Google uses a field
private final LocalServiceTestHelper helper
= new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());