diff --git a/CryptoExchange.Net/Testing/RestIntergrationTest.cs b/CryptoExchange.Net/Testing/RestIntergrationTest.cs
new file mode 100644
index 0000000..bb2fe69
--- /dev/null
+++ b/CryptoExchange.Net/Testing/RestIntergrationTest.cs
@@ -0,0 +1,100 @@
+using CryptoExchange.Net.Objects;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Diagnostics;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+
+namespace CryptoExchange.Net.Testing
+{
+ ///
+ /// Base class for executing REST API integration tests
+ ///
+ /// Client type
+ public abstract class RestIntergrationTest
+ {
+ ///
+ /// Get a client instance
+ ///
+ ///
+ ///
+ public abstract TClient GetClient(ILoggerFactory loggerFactory);
+
+ ///
+ /// Whether the test should be run. By default integration tests aren't executed, can be set to true to force execution.
+ ///
+ public virtual bool Run { get; set; }
+
+ ///
+ /// Whether API credentials are provided and thus authenticated calls can be executed. Should be set in the GetClient implementation.
+ ///
+ public bool Authenticated { get; set; }
+
+ ///
+ /// Create a client
+ ///
+ ///
+ protected TClient CreateClient()
+ {
+ var fact = new LoggerFactory();
+ fact.AddProvider(new TraceLoggerProvider());
+ return GetClient(fact);
+ }
+
+ ///
+ /// Check if integration tests should be executed
+ ///
+ ///
+ protected bool ShouldRun()
+ {
+ var integrationTests = Environment.GetEnvironmentVariable("INTEGRATION");
+ if (!Run && integrationTests != "1")
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Execute a REST endpoint call and check for any errors or warnings.
+ ///
+ /// Type of response
+ /// The call expression
+ /// Whether this is an authenticated request
+ public async Task RunAndCheckResult(Expression>>> expression, bool authRequest)
+ {
+ if (!ShouldRun())
+ return;
+
+ var client = CreateClient();
+
+ var expressionBody = (MethodCallExpression)expression.Body;
+ if (authRequest && !Authenticated)
+ {
+ Debug.WriteLine($"Skipping {expressionBody.Method.Name}, not authenticated");
+ return;
+ }
+
+ var listener = new EnumValueTraceListener();
+ Trace.Listeners.Add(listener);
+
+ WebCallResult result;
+ try
+ {
+ result = await expression.Compile().Invoke(client).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"Method {expressionBody.Method.Name} threw an exception: " + ex.ToLogString());
+ }
+ finally
+ {
+ Trace.Listeners.Remove(listener);
+ }
+
+ if (!result.Success)
+ throw new Exception($"Method {expressionBody.Method.Name} returned error: " + result.Error);
+
+ Debug.WriteLine($"{expressionBody.Method.Name} {result}");
+ }
+ }
+}