Merge pull request #8618 from eddumelendez:gh-8609

* pr/8618:
  Polish "Add @DataNeo4jTest" contribution
  Add @DataNeo4jTest
This commit is contained in:
Stephane Nicoll 2017-04-02 16:34:28 +02:00
commit 13e5d98560
13 changed files with 664 additions and 0 deletions

View File

@ -5710,6 +5710,59 @@ A list of the auto-configuration that is enabled by `@DataMongoTest` can be
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-neo4j-test]]
==== Auto-configured Data Neo4j tests
`@DataNeo4jTest` can be used if you want to test Neo4j applications. By default, it will
use an in-memory embedded Neo4j (if the embedded driver is available), scan for
`@NodeEntity` classes and configure Spring Data Neo4j repositories. Regular `@Component`
beans will not be loaded into the `ApplicationContext`:
[source,java,indent=0]
----
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@DataNeo4jTest
public class ExampleDataNeo4jTests {
@Autowired
private YourRepository repository;
//
}
----
Data Neo4j tests are transactional and rollback at the end of each test by default,
see the {spring-reference}#testcontext-tx-enabling-transactions[relevant section] in the
Spring Reference Documentation for more details. If that's not what you want, you can
disable transaction management for a test or for the whole class as follows:
[source,java,indent=0]
----
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringRunner.class)
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {
}
----
A list of the auto-configuration that is enabled by `@DataNeo4jTest` can be
<<appendix-test-auto-configuration#test-auto-configuration,found in the appendix>>.
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client]]
==== Auto-configured REST clients
The `@RestClientTest` annotation can be used if you want to test REST clients. By default

View File

@ -121,6 +121,11 @@
<artifactId>spring-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data Neo4j
* tests. Most tests should consider using {@link DataNeo4jTest @DataNeo4jTest} rather
* than using this annotation directly.
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataNeo4j {
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.transaction.annotation.Transactional;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Neo4j test. Can be used when a test focuses <strong>only</strong> on
* Neo4j components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to Neo4j tests.
* <p>
* By default, tests annotated with {@code @DataNeo4jTest} will use an embedded in-memory
* Neo4j process (if available). They will also be transactional with the usual
* test-related semantics (i.e. rollback by default).
*
* @author Eddú Meléndez
* @author Stephane Nicoll
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataNeo4jTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataNeo4j
@ImportAutoConfiguration
public @interface DataNeo4jTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataNeo4jTest @DataNeo4jTest}.
*
* @author Eddú Meléndez
*/
class DataNeo4jTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataNeo4jTest annotation;
DataNeo4jTypeExcludeFilter(final Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataNeo4jTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@ -23,6 +23,12 @@ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration
# AutoConfigureDataNeo4j auto-configuration imports
org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j=\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureJdbc auto-configuration imports
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\

View File

@ -0,0 +1,74 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.neo4j.ogm.session.Session;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test for {@link DataNeo4jTest}.
*
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
@RunWith(SpringRunner.class)
@DataNeo4jTest
public class DataNeo4jTestIntegrationTests {
@Rule
public Neo4jTestServer server = new Neo4jTestServer(
new String[] { "org.springframework.boot.test.autoconfigure.data.neo4j" });
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired
private Session session;
@Autowired
private ExampleRepository exampleRepository;
@Autowired
private ApplicationContext applicationContext;
@Test
public void testRepository() {
ExampleGraph exampleGraph = new ExampleGraph();
exampleGraph.setDescription("Look, new @DataNeo4jTest!");
assertThat(exampleGraph.getId()).isNull();
ExampleGraph savedGraph = this.exampleRepository.save(exampleGraph);
assertThat(savedGraph.getId()).isNotNull();
assertThat(this.session.countEntitiesOfType(ExampleGraph.class)).isEqualTo(1);
}
@Test
public void didNotInjectExampleService() {
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.applicationContext.getBean(ExampleService.class);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test with custom include filter for {@link DataNeo4jTest}.
*
* @author Eddú Meléndez
*/
@RunWith(SpringRunner.class)
@DataNeo4jTest(includeFilters = @Filter(Service.class))
public class DataNeo4jTestWithIncludeFilterIntegrationTests {
@Rule
public Neo4jTestServer server = new Neo4jTestServer(
new String[]{"org.springframework.boot.test.autoconfigure.data.neo4j"});
@Autowired
private ExampleService service;
@Test
public void testService() {
assertThat(this.service.hasNode(ExampleGraph.class)).isFalse();
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Property;
/**
* Example graph used with {@link DataNeo4jTest} tests.
*
* @author Eddú Meléndez
*/
@NodeEntity
public class ExampleGraph {
@GraphId
private Long id;
@Property
private String description;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Example {@link SpringBootApplication} used with {@link DataNeo4jTest} tests.
*
* @author Eddú Meléndez
*/
@SpringBootApplication
public class ExampleNeo4jApplication {
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.springframework.data.neo4j.repository.Neo4jRepository;
/**
* Example repository used with {@link DataNeo4jTest} tests.
*
* @author Eddú Meléndez
*/
public interface ExampleRepository extends Neo4jRepository<ExampleGraph, Long> {
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.neo4j.ogm.session.Session;
import org.springframework.stereotype.Service;
/**
* Example service used with {@link DataNeo4jTest} tests.
*
* @author Eddú Meléndez
*/
@Service
public class ExampleService {
private final Session session;
public ExampleService(Session session) {
this.session = session;
}
public boolean hasNode(Class<?> clazz) {
return this.session.countEntitiesOfType(clazz) == 1;
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.data.neo4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.neo4j.ogm.config.Configuration;
import org.neo4j.ogm.session.SessionFactory;
/**
* {@link TestRule} for working with an optional Neo4j server running on localhost. Make
* sure to disable authentication if you haven't done so already.
*
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
public class Neo4jTestServer implements TestRule {
private static final Log logger = LogFactory.getLog(Neo4jTestServer.class);
private SessionFactory sessionFactory;
private String[] packages;
public Neo4jTestServer(String[] packages) {
this.packages = packages;
}
@Override
public Statement apply(Statement base, Description description) {
try {
this.sessionFactory = createSessionFactory();
return new Neo4jStatement(base, this.sessionFactory);
}
catch (Exception ex) {
logger.error("No Neo4j server available", ex);
return new SkipStatement();
}
}
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
private SessionFactory createSessionFactory() {
Configuration configuration = new Configuration.Builder()
.uri("bolt://localhost:7687").build();
SessionFactory sessionFactory = new SessionFactory(configuration, this.packages);
testConnection(sessionFactory);
return sessionFactory;
}
private void testConnection(SessionFactory sessionFactory) {
sessionFactory.openSession().beginTransaction().close();
}
private static class Neo4jStatement extends Statement {
private final Statement base;
private final SessionFactory sessionFactory;
Neo4jStatement(Statement base, SessionFactory sessionFactory) {
this.base = base;
this.sessionFactory = sessionFactory;
}
@Override
public void evaluate() throws Throwable {
try {
this.base.evaluate();
}
finally {
try {
this.sessionFactory.close();
}
catch (Exception ex) {
logger.warn("Exception while trying to cleanup neo4j resource", ex);
}
}
}
}
private static class SkipStatement extends Statement {
@Override
public void evaluate() throws Throwable {
Assume.assumeTrue("Skipping test due to Neo4j SessionFactory"
+ " not being available", false);
}
}
}