diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml
index 47a6313996..f4b28b8a93 100644
--- a/spring-boot-test/pom.xml
+++ b/spring-boot-test/pom.xml
@@ -122,6 +122,11 @@
spring-boot-test-support
test
+
+ ch.qos.logback
+ logback-classic
+ test
+
org.apache.tomcat.embed
tomcat-embed-core
@@ -143,6 +148,16 @@
kotlin-runtime
test
+
+ org.slf4j
+ jcl-over-slf4j
+ test
+
+
+ org.slf4j
+ slf4j-api
+ test
+
org.spockframework
spock-core
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactory.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactory.java
new file mode 100644
index 0000000000..d3846ab240
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactory.java
@@ -0,0 +1,90 @@
+/*
+ * 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.json;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.test.context.ContextConfigurationAttributes;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.ContextCustomizerFactory;
+import org.springframework.test.context.MergedContextConfiguration;
+
+/**
+ * A {@link ContextCustomizerFactory} that produces a {@link ContextCustomizer} that warns
+ * the user when multiple occurrences of {@code JSONObject} are found on the class path.
+ *
+ * @author Andy Wilkinson
+ */
+class DuplicateJsonObjectContextCustomizerFactory implements ContextCustomizerFactory {
+
+ @Override
+ public ContextCustomizer createContextCustomizer(Class> testClass,
+ List configAttributes) {
+ return new DuplicateJsonObjectContextCustomizer();
+ }
+
+ private static class DuplicateJsonObjectContextCustomizer
+ implements ContextCustomizer {
+
+ private final Log logger = LogFactory
+ .getLog(DuplicateJsonObjectContextCustomizer.class);
+
+ @Override
+ public void customizeContext(ConfigurableApplicationContext context,
+ MergedContextConfiguration mergedConfig) {
+ List jsonObjects = findJsonObjects();
+ if (jsonObjects.size() > 1) {
+ logDuplicateJsonObjectsWarning(jsonObjects);
+ }
+ }
+
+ private List findJsonObjects() {
+ List jsonObjects = new ArrayList();
+ try {
+ Enumeration resources = getClass().getClassLoader()
+ .getResources("org/json/JSONObject.class");
+ while (resources.hasMoreElements()) {
+ jsonObjects.add(resources.nextElement());
+ }
+ }
+ catch (Exception ex) {
+ // Continue
+ }
+ return jsonObjects;
+ }
+
+ private void logDuplicateJsonObjectsWarning(List jsonObjects) {
+ StringBuilder message = new StringBuilder("\n\nFound multiple occurrences of"
+ + " org.json.JSONObject on the class path:\n\n");
+ for (URL jsonObject : jsonObjects) {
+ message.append("\t" + jsonObject + "\n");
+ }
+ message.append("\nYou may wish to exclude one of them to ensure"
+ + " predictable runtime behaviour\n");
+ this.logger.warn(message);
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/main/resources/META-INF/spring.factories b/spring-boot-test/src/main/resources/META-INF/spring.factories
index 3a9340c3bc..96e7abd05c 100644
--- a/spring-boot-test/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-test/src/main/resources/META-INF/spring.factories
@@ -3,6 +3,7 @@ org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.context.ImportsContextCustomizerFactory,\
org.springframework.boot.test.context.SpringBootTestContextCustomizerFactory,\
org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory,\
+org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory,\
org.springframework.boot.test.web.reactive.WebTestClientContextCustomizerFactory
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactoryTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactoryTests.java
new file mode 100644
index 0000000000..53e5f4ed66
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/DuplicateJsonObjectContextCustomizerFactoryTests.java
@@ -0,0 +1,49 @@
+/*
+ * 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.json;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.boot.junit.runner.classpath.ClassPathOverrides;
+import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner;
+import org.springframework.boot.test.rule.OutputCapture;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link DuplicateJsonObjectContextCustomizerFactory}.
+ *
+ * @author Andy Wilkinson
+ */
+@RunWith(ModifiedClassPathRunner.class)
+@ClassPathOverrides("org.json:json:20140107")
+public class DuplicateJsonObjectContextCustomizerFactoryTests {
+
+ @Rule
+ public OutputCapture output = new OutputCapture();
+
+ @Test
+ public void warningForMultipleVersions() {
+ new DuplicateJsonObjectContextCustomizerFactory()
+ .createContextCustomizer(null, null).customizeContext(null, null);
+ assertThat(this.output.toString()).contains(
+ "Found multiple occurrences of org.json.JSONObject on the class path:");
+ }
+
+}