Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor test infrastructure #1444

Merged
merged 1 commit into from
Dec 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ image: Visual Studio 2017
environment:
COVERALLS_REPO_TOKEN:
secure: T0PmP8uyzCseacBCDRBlti2y9Tz5DL6fknea0MKWvbPYrzADmLY2/5kOTfYIsPUk
# If you bump this, don't forget to bump `MinimumMockVersion` in `BaseStripeTest.cs` as well.
# If you bump this, don't forget to bump `MinimumMockVersion` in `StripeMockFixture.cs` as well.
STRIPE_MOCK_VERSION: 0.39.0

deploy:
Expand Down
188 changes: 47 additions & 141 deletions src/StripeTests/BaseStripeTest.cs
Original file line number Diff line number Diff line change
@@ -1,106 +1,44 @@
namespace StripeTests
{
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;

using Moq;
using Moq.Protected;
using Stripe;
using Xunit;

[Collection("stripe-mock tests")]
public class BaseStripeTest
{
/// <value>Minimum required version of stripe-mock</value>
/// <remarks>
/// If you bump this, don't forget to bump `STRIPE_MOCK_VERSION` in appveyor.yml as well.
/// </remarks>
private const string MockMinimumVersion = "0.39.0";

private static Mock<HttpClientHandler> mockHandler;

private static string port;

// Lazy initializer to ensure that initialization is run only once even when running tests
// in parallel.
private static Lazy<object> initializer = new Lazy<object>(InitStripeMock);

public BaseStripeTest()
: this(null, null)
{
// This triggers the lazy initialization. We don't actually care about the value of
// initialized (it will be null anyway), but simply writing `initializer.Value` is not
// a valid statement in C#.
var initialized = initializer.Value;
}

// Reset the mock before each test
mockHandler.Invocations.Clear();
public BaseStripeTest(StripeMockFixture stripeMockFixture)
: this(stripeMockFixture, null)
{
}

/// <summary>
/// Gets fixture data from stripe-mock for a resource expected to be at the given API path.
/// stripe-mock ignores whether IDs are actually valid, so it's only important to make sure
/// that the route exists, rather than the actual resource. It's common to use a symbolic
/// ID stand-in like <code>ch_123</code>
/// </summary>
/// <param name="path">API path to use to get a fixture for stripe-mock</param>
/// <returns>Fixture data encoded as JSON</returns>
protected static string GetFixture(string path)
public BaseStripeTest(MockHttpClientFixture mockHttpClientFixture)
: this(null, mockHttpClientFixture)
{
return GetFixture(path, null);
}

/// <summary>
/// Gets fixture data with expansions specified. Expansions are specified the same way as
/// they are in the normal API like <code>customer</code> or <code>data.customer</code>.
/// Use the special <code>*</code> character to specify that all fields should be
/// expanded.
/// </summary>
/// <param name="path">API path to use to get a fixture for stripe-mock</param>
/// <param name="expansions">Set of expansions that should be applied</param>
/// <returns>Fixture data encoded as JSON</returns>
protected static string GetFixture(string path, string[] expansions)
public BaseStripeTest(StripeMockFixture stripeMockFixture, MockHttpClientFixture mockHttpClientFixture)
{
string url = $"http://localhost:{port}{path}";
this.StripeMockFixture = stripeMockFixture;
this.MockHttpClientFixture = mockHttpClientFixture;

if (expansions != null)
if (this.MockHttpClientFixture != null)
{
string query = string.Join("&", expansions.Select(x => $"expand[]={x}").ToArray());
url += $"?{query}";
// Reset the mock before each test
this.MockHttpClientFixture.Reset();
}
}

using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization
= new System.Net.Http.Headers.AuthenticationHeaderValue(
"Bearer",
"sk_test_123");

HttpResponseMessage response;

try
{
response = client.GetAsync(url).Result;
}
catch (Exception)
{
throw new Exception(
$"Couldn't reach stripe-mock at `localhost:{port}`. "
+ "Is it running? Please see README for setup instructions.");
}
protected MockHttpClientFixture MockHttpClientFixture { get; }

if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception(
$"stripe-mock returned status code: {response.StatusCode}.");
}

return response.Content.ReadAsStringAsync().Result;
}
}
protected StripeMockFixture StripeMockFixture { get; }

/// <summary>
/// Gets a resource file and returns its contents in a string.
Expand All @@ -110,83 +48,51 @@ protected static string GetFixture(string path, string[] expansions)
protected static string GetResourceAsString(string path)
{
var fullpath = "StripeTests.Resources." + path;
var json = new StreamReader(
var contents = new StreamReader(
typeof(BaseStripeTest).GetTypeInfo().Assembly.GetManifestResourceStream(fullpath),
Encoding.UTF8).ReadToEnd();

return json;
}

protected void AssertRequest(HttpMethod method, string path)
{
mockHandler.Protected().Verify(
"SendAsync",
Times.Once(),
ItExpr.Is<HttpRequestMessage>(m =>
m.Method == method &&
m.RequestUri.AbsolutePath == path),
ItExpr.IsAny<CancellationToken>());
return contents;
}

/// <summary>
/// Checks that stripe-mock is running and that it's a recent enough version.
/// Asserts that a single HTTP request was made with the specified method and path.
/// </summary>
private static object InitStripeMock()
protected void AssertRequest(HttpMethod method, string path)
{
port = Environment.GetEnvironmentVariable("STRIPE_MOCK_PORT") ?? "12111";
string url = $"http://localhost:{port}";

using (HttpClient client = new HttpClient())
if (this.MockHttpClientFixture == null)
{
HttpResponseMessage response;

try
{
response = client.GetAsync(url).Result;
}
catch (Exception)
{
throw new Exception(
$"Couldn't reach stripe-mock at `localhost:{port}`. "
+ "Is it running? Please see README for setup instructions.");
}

string version = response.Headers.GetValues("Stripe-Mock-Version").FirstOrDefault();

if (!version.Equals("master") &&
(CompareVersions(version, MockMinimumVersion) > 0))
{
throw new Exception(
$"Your version of stripe-mock ({version}) is too old. The minimum "
+ $"version to run this test suite is {MockMinimumVersion}. Please see its "
+ "repository for upgrade instructions.");
}
throw new StripeTestException(
"AssertRequest called from a test class that doesn't have access to "
+ "MockHttpClientFixture. Make sure that the constructor for "
+ $"{this.GetType().Name} receives MockHttpClientFixture and calls the "
+ "base constructor.");
}

StripeConfiguration.SetApiBase($"http://localhost:{port}/v1");
StripeConfiguration.SetFilesBase($"http://localhost:{port}/v1");
StripeConfiguration.SetApiKey("sk_test_123");

mockHandler = new Mock<HttpClientHandler>
{
CallBase = true
};
StripeConfiguration.HttpMessageHandler = mockHandler.Object;

return null;
this.MockHttpClientFixture.AssertRequest(method, path);
}

/// <summary>
/// Compares two version strings.
/// Gets fixture data with expansions specified. Expansions are specified the same way as
/// they are in the normal API like <code>customer</code> or <code>data.customer</code>.
/// Use the special <code>*</code> character to specify that all fields should be
/// expanded.
/// </summary>
/// <param name="a">A version string (e.g. "1.2.3").</param>
/// <param name="b">Another version string.</param>
/// <returns>-1 if a > b, 1 if a < b, 0 if a == b</returns>
private static int CompareVersions(string a, string b)
/// <param name="path">API path to use to get a fixture for stripe-mock</param>
/// <param name="expansions">Set of expansions that should be applied</param>
/// <returns>Fixture data encoded as JSON</returns>
protected string GetFixture(string path, string[] expansions = null)
{
var version1 = new Version(a);
var version2 = new Version(b);
return version2.CompareTo(version1);
if (this.StripeMockFixture == null)
{
throw new StripeTestException(
"GetFixture called from a test class that doesn't have access to "
+ "StripeMockFixture. Make sure that the constructor for "
+ $"{this.GetType().Name} receives StripeMockFixture and calls the "
+ "base constructor.");
}

return this.StripeMockFixture.GetFixture(path, expansions);
}
}
}
9 changes: 7 additions & 2 deletions src/StripeTests/Entities/Accounts/AccountTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace StripeTests

public class AccountTest : BaseStripeTest
{
public AccountTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/accounts/acct_123");
string json = this.GetFixture("/v1/accounts/acct_123");
var account = JsonConvert.DeserializeObject<Account>(json);
Assert.NotNull(account);
Assert.IsType<Account>(account);
Expand All @@ -25,7 +30,7 @@ public void DeserializeWithExpansions()
"business_logo",
};

string json = GetFixture("/v1/accounts/acct_123", expansions);
string json = this.GetFixture("/v1/accounts/acct_123", expansions);
var account = JsonConvert.DeserializeObject<Account>(json);

Assert.NotNull(account);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace StripeTests

public class ApplePayDomainTest : BaseStripeTest
{
public ApplePayDomainTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/apple_pay/domains/apwc_123");
string json = this.GetFixture("/v1/apple_pay/domains/apwc_123");
var domain = JsonConvert.DeserializeObject<ApplePayDomain>(json);
Assert.NotNull(domain);
Assert.IsType<ApplePayDomain>(domain);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace StripeTests

public class ApplicationFeeRefundTest : BaseStripeTest
{
public ApplicationFeeRefundTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/application_fees/fee_123/refunds/fr_123");
string json = this.GetFixture("/v1/application_fees/fee_123/refunds/fr_123");
var applicationFeeRefund = JsonConvert.DeserializeObject<ApplicationFeeRefund>(json);
Assert.NotNull(applicationFeeRefund);
Assert.IsType<ApplicationFeeRefund>(applicationFeeRefund);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace StripeTests

public class ApplicationFeeTest : BaseStripeTest
{
public ApplicationFeeTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/application_fees/fee_123");
string json = this.GetFixture("/v1/application_fees/fee_123");
var applicationFee = JsonConvert.DeserializeObject<ApplicationFee>(json);
Assert.NotNull(applicationFee);
Assert.IsType<ApplicationFee>(applicationFee);
Expand All @@ -29,7 +34,7 @@ public void DeserializeWithExpansions()
"originating_transaction",
};

string json = GetFixture("/v1/application_fees/fee_123", expansions);
string json = this.GetFixture("/v1/application_fees/fee_123", expansions);
var applicationFee = JsonConvert.DeserializeObject<ApplicationFee>(json);
Assert.NotNull(applicationFee);
Assert.IsType<ApplicationFee>(applicationFee);
Expand Down
7 changes: 6 additions & 1 deletion src/StripeTests/Entities/Balance/BalanceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace StripeTests

public class BalanceTest : BaseStripeTest
{
public BalanceTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/balance");
string json = this.GetFixture("/v1/balance");
var balance = JsonConvert.DeserializeObject<Balance>(json);
Assert.NotNull(balance);
Assert.IsType<Balance>(balance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ namespace StripeTests

public class BalanceTransactionTest : BaseStripeTest
{
public BalanceTransactionTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
{
}

[Fact]
public void Deserialize()
{
string json = GetFixture("/v1/balance/history/txn_123");
string json = this.GetFixture("/v1/balance/history/txn_123");
var balanceTransaction = JsonConvert.DeserializeObject<BalanceTransaction>(json);
Assert.NotNull(balanceTransaction);
Assert.IsType<BalanceTransaction>(balanceTransaction);
Expand Down
Loading