You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/07/17 09:52:42 UTC

[skywalking] branch zipkin updated: Add tests for Zipkin Context Manager.

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch zipkin
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/zipkin by this push:
     new dbd97b5  Add tests for Zipkin Context Manager.
dbd97b5 is described below

commit dbd97b5f3265aadd0948530cae9f5710ba592247
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Fri Jul 17 17:52:24 2020 +0800

    Add tests for Zipkin Context Manager.
---
 .../reporter/zipkin/ZipkinContextManager.java      |  9 ++-
 .../apm/plugin/reporter/zipkin/ZipkinSpan.java     | 14 ++--
 .../reporter/zipkin/ZipkinTraceReporter.java       |  3 +
 .../reporter/zipkin/ZipkinTracerContext.java       | 19 +++--
 ...Test.java => ContextIsolationInThreadTest.java} | 26 ++++---
 .../plugin/reporter/zipkin/ContextManageTest.java  | 80 ++++++++++++++++++++++
 ...kinTracerContextTest.java => SpanAttrTest.java} | 59 +++++++++-------
 .../apm/plugin/reporter/zipkin/ZipkinTest.java     | 37 +++++++---
 8 files changed, 192 insertions(+), 55 deletions(-)

diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinContextManager.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinContextManager.java
index 80eafcd..f1ac278 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinContextManager.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinContextManager.java
@@ -22,6 +22,7 @@ import org.apache.skywalking.apm.agent.core.boot.OverrideImplementor;
 import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
 import org.apache.skywalking.apm.agent.core.context.AbstractTracerContext;
 import org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService;
+import org.apache.skywalking.apm.agent.core.remote.TraceSegmentServiceClient;
 
 /**
  * ZipkinContextManager used Brave APIs to manage the Zipkin tracing context, including span start/stop/tag/log,
@@ -33,7 +34,13 @@ public class ZipkinContextManager extends ContextManagerExtendService {
 
     @Override
     public void prepare() {
-        zipkinTraceReporter = ServiceManager.INSTANCE.findService(ZipkinTraceReporter.class);
+        zipkinTraceReporter = (ZipkinTraceReporter) ServiceManager.INSTANCE.findService(
+            TraceSegmentServiceClient.class);
+    }
+
+    @Override
+    public void shutdown() {
+        zipkinTraceReporter = null;
     }
 
     /**
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinSpan.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinSpan.java
index e207ffd..85fe619 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinSpan.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinSpan.java
@@ -85,6 +85,15 @@ public class ZipkinSpan implements AbstractSpan {
         return this;
     }
 
+    /**
+     * Zipkin isn't willing to accept the logs, in the meaning while in the SkyWalking plugin typically only log the
+     * error stack. The exception name has been recorded through {@link #log(Throwable)}
+     */
+    @Override
+    public AbstractSpan log(final long timestamp, final Map<String, ?> event) {
+        return this;
+    }
+
     @Override
     public AbstractSpan log(final Throwable t) {
         span.error(t);
@@ -107,11 +116,6 @@ public class ZipkinSpan implements AbstractSpan {
     }
 
     @Override
-    public AbstractSpan log(final long timestamp, final Map<String, ?> event) {
-        return null;
-    }
-
-    @Override
     public AbstractSpan setOperationName(final String operationName) {
         span.name(operationName);
         return this;
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTraceReporter.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTraceReporter.java
index 8d2bf3c..3215c94 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTraceReporter.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTraceReporter.java
@@ -68,5 +68,8 @@ public class ZipkinTraceReporter extends TraceSegmentServiceClient {
         tracing.close();
         zipkinSpanHandler.close();
         sender.close();
+
+        // This is just cleaning the current thread context, mostly for tests.
+        currentTraceContext.clear();
     }
 }
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContext.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContext.java
index 3ba06f0..cee544c 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContext.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/main/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContext.java
@@ -42,7 +42,11 @@ import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
  * waiting for {@link #asyncStop(AsyncSpan)}.
  */
 public class ZipkinTracerContext implements AbstractTracerContext {
-    private static String B3_NAME = "b3";
+    static String B3_ACROSS_THREAD = "B3";
+    static String B3_NAME_SPAN_ID = "X-B3-SpanId";
+    static String B3_NAME_SPAN_PARENT_SPAN_ID = "X-B3-ParentSpanId";
+    static String B3_NAME_SAMPLED = "X-B3-Sampled";
+    static String B3_NAME_TRACE_ID = "X-B3-TraceId";
 
     /**
      * Running span cache of the current Zipkin context. This takes the responsibility of determining when this context
@@ -72,8 +76,13 @@ public class ZipkinTracerContext implements AbstractTracerContext {
 
     @Override
     public void extract(final ContextCarrier carrier) {
-        carrier.setCustomKeys(B3_NAME);
-        tracing.propagation().extractor((request, key) -> carrier.readCustomContext(key)).extract(null);
+        carrier.setCustomKeys(B3_NAME_SPAN_ID);
+        carrier.setCustomKeys(B3_NAME_SPAN_PARENT_SPAN_ID);
+        carrier.setCustomKeys(B3_NAME_SAMPLED);
+        carrier.setCustomKeys(B3_NAME_TRACE_ID);
+        tracing.propagation().extractor(
+            (request, key) -> carrier.readCustomContext(key)
+        ).extract(new Object());
         this.correlationContext.extract(carrier);
     }
 
@@ -81,13 +90,13 @@ public class ZipkinTracerContext implements AbstractTracerContext {
     public ContextSnapshot capture() {
         final TraceContext traceContext = tracing.currentTraceContext().get();
         ContextSnapshot contextSnapshot = new ContextSnapshot(correlationContext);
-        contextSnapshot.addCustomContext(B3_NAME, traceContext);
+        contextSnapshot.addCustomContext(B3_ACROSS_THREAD, traceContext);
         return contextSnapshot;
     }
 
     @Override
     public void continued(final ContextSnapshot snapshot) {
-        final TraceContext traceContext = (TraceContext) snapshot.readCustomContext(B3_NAME);
+        final TraceContext traceContext = (TraceContext) snapshot.readCustomContext(B3_ACROSS_THREAD);
         tracing.currentTraceContext().newScope(traceContext);
     }
 
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextIsolationInThreadTest.java
similarity index 82%
copy from apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java
copy to apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextIsolationInThreadTest.java
index b187ffb..639d6cb 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextIsolationInThreadTest.java
@@ -26,25 +26,22 @@ import org.junit.Test;
 import org.powermock.reflect.Whitebox;
 import zipkin2.Span;
 
-public class ZipkinTracerContextTest extends ZipkinTest {
+public class ContextIsolationInThreadTest extends ZipkinTest {
     @Test
-    public void testContextIsolationInThread() {
-        final ZipkinTraceReporter zipkinTraceReporter = new ZipkinTraceReporter();
-        zipkinTraceReporter.boot();
-
-        ZipkinContextManager manager = new ZipkinContextManager();
-        Whitebox.setInternalState(manager, "zipkinTraceReporter", zipkinTraceReporter);
-        final AbstractTracerContext traceContext = manager.createTraceContext("/span1", true);
+    public void test() {
+        final AbstractTracerContext traceContext = newTracerContext("/span1");
         Assert.assertTrue(traceContext instanceof ZipkinTracerContext);
 
         final AbstractSpan span1 = traceContext.createEntrySpan("span1");
+        Assert.assertTrue(span1.isEntry());
         final AbstractSpan span2 = traceContext.createLocalSpan("span2");
         final AbstractSpan span3 = traceContext.createExitSpan("span3", "127.0.0.1:8080");
+        Assert.assertTrue(span3.isExit());
         traceContext.stopSpan(span3);
         traceContext.stopSpan(span2);
         traceContext.stopSpan(span1);
 
-        List<List<Span>> traces = readTracesUntilTimeout(10);
+        List<List<Span>> traces = readTracesUntilTimeout(10, 1, 3);
         Assert.assertEquals(1, traces.size());
         List<Span> spans = traces.get(0);
         Assert.assertEquals(3, spans.size());
@@ -54,10 +51,19 @@ public class ZipkinTracerContextTest extends ZipkinTest {
         final AbstractSpan span4 = traceContext.createEntrySpan("span4");
         traceContext.stopSpan(span4);
 
-        traces = readTracesUntilTimeout(10);
+        traces = readTracesUntilTimeout(10, 2, 4);
         Assert.assertEquals(2, traces.size());
         spans = traces.get(1);
         Assert.assertEquals(1, spans.size());
         Assert.assertEquals("span4", spans.get(0).name());
     }
+
+    private AbstractTracerContext newTracerContext(String entranceEndpoint) {
+        final ZipkinTraceReporter zipkinTraceReporter = new ZipkinTraceReporter();
+        zipkinTraceReporter.boot();
+
+        ZipkinContextManager manager = new ZipkinContextManager();
+        Whitebox.setInternalState(manager, "zipkinTraceReporter", zipkinTraceReporter);
+        return manager.createTraceContext(entranceEndpoint, true);
+    }
 }
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextManageTest.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextManageTest.java
new file mode 100644
index 0000000..6b38566
--- /dev/null
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ContextManageTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.skywalking.apm.plugin.reporter.zipkin;
+
+import java.util.List;
+import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
+import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+import zipkin2.Span;
+
+import static org.apache.skywalking.apm.plugin.reporter.zipkin.ZipkinTracerContext.B3_NAME_SAMPLED;
+import static org.apache.skywalking.apm.plugin.reporter.zipkin.ZipkinTracerContext.B3_NAME_SPAN_ID;
+import static org.apache.skywalking.apm.plugin.reporter.zipkin.ZipkinTracerContext.B3_NAME_TRACE_ID;
+
+public class ContextManageTest extends ZipkinTest {
+    @AfterClass
+    public static void shutdown() {
+        ServiceManager.INSTANCE.shutdown();
+    }
+
+    @Test
+    public void testInThread() {
+        final AbstractSpan entrySpan = ContextManager.createEntrySpan("/span", new ContextCarrier());
+        Assert.assertTrue(entrySpan instanceof ZipkinSpan);
+        ContextManager.stopSpan();
+
+        final List<List<Span>> traces = readTracesUntilTimeout(10, 1, 1);
+        Assert.assertEquals(1, traces.size());
+        final List<Span> spans = traces.get(0);
+        Assert.assertEquals(1, spans.size());
+        Assert.assertEquals("/span", spans.get(0).name());
+    }
+
+    @Test
+    public void testAcrossProcess() {
+        final ContextCarrier header = new ContextCarrier();
+        ContextManager.createLocalSpan("local method");
+        ContextManager.createExitSpan("/rpc-client", header, "127.0.0.1");
+        ContextManager.stopSpan();
+        ContextManager.stopSpan();
+
+        List<List<Span>> traces = readTracesUntilTimeout(10, 1, 2);
+        Assert.assertEquals(1, traces.size());
+
+        Assert.assertNotNull(header.readCustomContext(B3_NAME_SPAN_ID));
+        Assert.assertNotNull(header.readCustomContext(B3_NAME_SAMPLED));
+        Assert.assertNotNull(header.readCustomContext(B3_NAME_TRACE_ID));
+
+        ContextManager.createEntrySpan("/rpc-server", header);
+        ContextManager.stopSpan();
+
+        traces = readTracesUntilTimeout(10, 1, 3);
+        Assert.assertEquals(1, traces.size());
+        final List<Span> spans = traces.get(0);
+        Assert.assertEquals(zipkin2.Span.Kind.CLIENT, spans.get(0).kind());
+        Assert.assertEquals("/rpc-client", spans.get(0).name());
+        Assert.assertEquals(zipkin2.Span.Kind.SERVER, spans.get(2).kind());
+        Assert.assertEquals("/rpc-server", spans.get(2).name());
+    }
+}
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/SpanAttrTest.java
similarity index 50%
rename from apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java
rename to apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/SpanAttrTest.java
index b187ffb..ad3121c 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTracerContextTest.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/SpanAttrTest.java
@@ -18,46 +18,57 @@
 
 package org.apache.skywalking.apm.plugin.reporter.zipkin;
 
+import java.util.HashMap;
 import java.util.List;
 import org.apache.skywalking.apm.agent.core.context.AbstractTracerContext;
+import org.apache.skywalking.apm.agent.core.context.tag.Tags;
 import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
 import org.junit.Assert;
 import org.junit.Test;
 import org.powermock.reflect.Whitebox;
 import zipkin2.Span;
 
-public class ZipkinTracerContextTest extends ZipkinTest {
+public class SpanAttrTest extends ZipkinTest {
     @Test
-    public void testContextIsolationInThread() {
-        final ZipkinTraceReporter zipkinTraceReporter = new ZipkinTraceReporter();
-        zipkinTraceReporter.boot();
-
-        ZipkinContextManager manager = new ZipkinContextManager();
-        Whitebox.setInternalState(manager, "zipkinTraceReporter", zipkinTraceReporter);
-        final AbstractTracerContext traceContext = manager.createTraceContext("/span1", true);
+    public void testSpan() {
+        final AbstractTracerContext traceContext = newTracerContext("/span1");
         Assert.assertTrue(traceContext instanceof ZipkinTracerContext);
 
         final AbstractSpan span1 = traceContext.createEntrySpan("span1");
-        final AbstractSpan span2 = traceContext.createLocalSpan("span2");
-        final AbstractSpan span3 = traceContext.createExitSpan("span3", "127.0.0.1:8080");
-        traceContext.stopSpan(span3);
-        traceContext.stopSpan(span2);
+        span1.tag(Tags.URL, "http://127.0.0.1:8080/query");
+        span1.tag(Tags.STATUS_CODE, "200");
+        span1.log(new RuntimeException());
+        span1.setComponent(ComponentsDefine.TOMCAT);
+        span1.setLayer(SpanLayer.HTTP);
+        // All these methods for now will be ignored, because these are not suitable or recommended in the Zipkin models.
+        span1.errorOccurred();
+        span1.log(System.currentTimeMillis(), new HashMap<>());
+        //
         traceContext.stopSpan(span1);
 
-        List<List<Span>> traces = readTracesUntilTimeout(10);
+        List<List<Span>> traces = readTracesUntilTimeout(10, 1, 1);
         Assert.assertEquals(1, traces.size());
-        List<Span> spans = traces.get(0);
-        Assert.assertEquals(3, spans.size());
-        Assert.assertEquals("span3", spans.get(0).name());
+        final List<Span> spans = traces.get(0);
+        Assert.assertEquals(1, spans.size());
+        final Span span = spans.get(0);
+        Assert.assertNotNull(span.traceId());
+        Assert.assertEquals(Span.Kind.SERVER, span.kind());
+        Assert.assertEquals(5, span.tags().size());
+        Assert.assertEquals("RuntimeException", span.tags().get("error"));
+        Assert.assertEquals("200", span.tags().get("status_code"));
+        Assert.assertEquals("http://127.0.0.1:8080/query", span.tags().get("url"));
+        Assert.assertEquals("Tomcat", span.tags().get("component"));
+        Assert.assertEquals("HTTP", span.tags().get("layer"));
+    }
 
-        // Scope should be clear automatically in traceContext#stopSpan.
-        final AbstractSpan span4 = traceContext.createEntrySpan("span4");
-        traceContext.stopSpan(span4);
+    private AbstractTracerContext newTracerContext(String entranceEndpoint) {
+        final ZipkinTraceReporter zipkinTraceReporter = new ZipkinTraceReporter();
+        zipkinTraceReporter.boot();
 
-        traces = readTracesUntilTimeout(10);
-        Assert.assertEquals(2, traces.size());
-        spans = traces.get(1);
-        Assert.assertEquals(1, spans.size());
-        Assert.assertEquals("span4", spans.get(0).name());
+        ZipkinContextManager manager = new ZipkinContextManager();
+        Whitebox.setInternalState(manager, "zipkinTraceReporter", zipkinTraceReporter);
+        return manager.createTraceContext(entranceEndpoint, true);
     }
 }
diff --git a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTest.java b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTest.java
index e66086b..b7ab683 100644
--- a/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTest.java
+++ b/apm-sniffer/optional-reporter-plugins/zipkin-reporter-plugin/src/test/java/org/apache/skywalking/apm/plugin/reporter/zipkin/ZipkinTest.java
@@ -18,27 +18,38 @@
 
 package org.apache.skywalking.apm.plugin.reporter.zipkin;
 
+import java.io.IOException;
 import java.util.List;
+import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
 import org.apache.skywalking.apm.agent.core.conf.Config;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
+import org.mockito.internal.util.reflection.Whitebox;
 import zipkin2.Span;
 import zipkin2.junit.ZipkinRule;
+import zipkin2.storage.InMemoryStorage;
 
 public class ZipkinTest {
-    @Rule
-    public ZipkinRule zipkin = new ZipkinRule();
+    private static boolean IS_BOOTED = false;
+    public static ZipkinRule ZIPKIN_SERVER = new ZipkinRule();
 
     @Before
-    public void setup() {
-        Config.Collector.BACKEND_SERVICE = zipkin.httpUrl() + "/api/v2/spans";
+    public void setup() throws IOException {
+        if (!IS_BOOTED) {
+            ZIPKIN_SERVER.start(9091);
+        }
+        Config.Collector.BACKEND_SERVICE = ZIPKIN_SERVER.httpUrl() + "/api/v2/spans";
         Config.Agent.SERVICE_NAME = "Zipkin-Core";
+        if (!IS_BOOTED) {
+            ServiceManager.INSTANCE.boot();
+            IS_BOOTED = true;
+        }
     }
 
     @After
     public void clean() {
+        ((InMemoryStorage) Whitebox.getInternalState(ZIPKIN_SERVER, "storage")).clear();
         Config.Collector.BACKEND_SERVICE = null;
         Config.Agent.SERVICE_NAME = null;
     }
@@ -47,7 +58,7 @@ public class ZipkinTest {
      * @param time * 2s = Max wait time
      * @return traces
      */
-    protected List<List<Span>> readTracesUntilTimeout(int time) {
+    protected List<List<Span>> readTracesUntilTimeout(int time, int expectedTraceSize, int expectedSpanSize) {
         List<List<Span>> traces = null;
         boolean received = false;
         for (int i = time; i >= 0; i--) {
@@ -55,10 +66,16 @@ public class ZipkinTest {
                 Thread.sleep(2000L);
             } catch (InterruptedException e) {
             }
-            traces = zipkin.getTraces();
-            if (traces.size() > 0) {
-                received = true;
-                break;
+            traces = ZIPKIN_SERVER.getTraces();
+            if (traces.size() == expectedTraceSize) {
+                int numOfSpan = 0;
+                for (final List<Span> spans : traces) {
+                    numOfSpan += spans.size();
+                }
+                if (numOfSpan == expectedSpanSize) {
+                    received = true;
+                    break;
+                }
             }
         }