diff options
Diffstat (limited to 'Timeline.Tests')
-rw-r--r-- | Timeline.Tests/Controllers/TokenControllerTest.cs | 4 | ||||
-rw-r--r-- | Timeline.Tests/Controllers/UserControllerTest.cs | 4 | ||||
-rw-r--r-- | Timeline.Tests/Helpers/UseCultureAttribute.cs | 91 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/I18nTest.cs | 63 | ||||
-rw-r--r-- | Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs | 25 | ||||
-rw-r--r-- | Timeline.Tests/UsernameValidatorUnitTest.cs | 8 |
6 files changed, 160 insertions, 35 deletions
diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs index 53b6c606..4a08ca0f 100644 --- a/Timeline.Tests/Controllers/TokenControllerTest.cs +++ b/Timeline.Tests/Controllers/TokenControllerTest.cs @@ -25,9 +25,7 @@ namespace Timeline.Tests.Controllers public TokenControllerTest()
{
- _controller = new TokenController(_mockUserService.Object,
- NullLogger<TokenController>.Instance, _mockClock,
- TestStringLocalizerFactory.Create().Create<TokenController>());
+ _controller = new TokenController(_mockUserService.Object, NullLogger<TokenController>.Instance, _mockClock);
}
public void Dispose()
diff --git a/Timeline.Tests/Controllers/UserControllerTest.cs b/Timeline.Tests/Controllers/UserControllerTest.cs index 781ec111..a9cce970 100644 --- a/Timeline.Tests/Controllers/UserControllerTest.cs +++ b/Timeline.Tests/Controllers/UserControllerTest.cs @@ -27,9 +27,7 @@ namespace Timeline.Tests.Controllers public UserControllerTest()
{
- _controller = new UserController(NullLogger<UserController>.Instance,
- _mockUserService.Object,
- TestStringLocalizerFactory.Create());
+ _controller = new UserController(NullLogger<UserController>.Instance, _mockUserService.Object);
}
public void Dispose()
diff --git a/Timeline.Tests/Helpers/UseCultureAttribute.cs b/Timeline.Tests/Helpers/UseCultureAttribute.cs new file mode 100644 index 00000000..f0064c01 --- /dev/null +++ b/Timeline.Tests/Helpers/UseCultureAttribute.cs @@ -0,0 +1,91 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading; +using Xunit.Sdk; + +// Copied from https://github.com/xunit/samples.xunit/blob/master/UseCulture/UseCultureAttribute.cs + +/// <summary> +/// Apply this attribute to your test method to replace the +/// <see cref="Thread.CurrentThread" /> <see cref="CultureInfo.CurrentCulture" /> and +/// <see cref="CultureInfo.CurrentUICulture" /> with another culture. +/// </summary> +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] +public class UseCultureAttribute : BeforeAfterTestAttribute +{ + readonly Lazy<CultureInfo> culture; + readonly Lazy<CultureInfo> uiCulture; + + CultureInfo originalCulture; + CultureInfo originalUICulture; + + /// <summary> + /// Replaces the culture and UI culture of the current thread with + /// <paramref name="culture" /> + /// </summary> + /// <param name="culture">The name of the culture.</param> + /// <remarks> + /// <para> + /// This constructor overload uses <paramref name="culture" /> for both + /// <see cref="Culture" /> and <see cref="UICulture" />. + /// </para> + /// </remarks> + public UseCultureAttribute(string culture) + : this(culture, culture) { } + + /// <summary> + /// Replaces the culture and UI culture of the current thread with + /// <paramref name="culture" /> and <paramref name="uiCulture" /> + /// </summary> + /// <param name="culture">The name of the culture.</param> + /// <param name="uiCulture">The name of the UI culture.</param> + public UseCultureAttribute(string culture, string uiCulture) + { + this.culture = new Lazy<CultureInfo>(() => new CultureInfo(culture, false)); + this.uiCulture = new Lazy<CultureInfo>(() => new CultureInfo(uiCulture, false)); + } + + /// <summary> + /// Gets the culture. + /// </summary> + public CultureInfo Culture { get { return culture.Value; } } + + /// <summary> + /// Gets the UI culture. + /// </summary> + public CultureInfo UICulture { get { return uiCulture.Value; } } + + /// <summary> + /// Stores the current <see cref="Thread.CurrentPrincipal" /> + /// <see cref="CultureInfo.CurrentCulture" /> and <see cref="CultureInfo.CurrentUICulture" /> + /// and replaces them with the new cultures defined in the constructor. + /// </summary> + /// <param name="methodUnderTest">The method under test</param> + public override void Before(MethodInfo methodUnderTest) + { + originalCulture = Thread.CurrentThread.CurrentCulture; + originalUICulture = Thread.CurrentThread.CurrentUICulture; + + Thread.CurrentThread.CurrentCulture = Culture; + Thread.CurrentThread.CurrentUICulture = UICulture; + + CultureInfo.CurrentCulture.ClearCachedData(); + CultureInfo.CurrentUICulture.ClearCachedData(); + } + + /// <summary> + /// Restores the original <see cref="CultureInfo.CurrentCulture" /> and + /// <see cref="CultureInfo.CurrentUICulture" /> to <see cref="Thread.CurrentPrincipal" /> + /// </summary> + /// <param name="methodUnderTest">The method under test</param> + public override void After(MethodInfo methodUnderTest) + { + Thread.CurrentThread.CurrentCulture = originalCulture; + Thread.CurrentThread.CurrentUICulture = originalUICulture; + + CultureInfo.CurrentCulture.ClearCachedData(); + CultureInfo.CurrentUICulture.ClearCachedData(); + } +} diff --git a/Timeline.Tests/IntegratedTests/I18nTest.cs b/Timeline.Tests/IntegratedTests/I18nTest.cs new file mode 100644 index 00000000..67bbea5c --- /dev/null +++ b/Timeline.Tests/IntegratedTests/I18nTest.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Mvc.Testing;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+using Timeline.Tests.Helpers;
+using Xunit;
+using FluentAssertions;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1054:Uri parameters should not be strings")]
+ public class I18nTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
+ {
+ private readonly TestApplication _testApp;
+ private readonly HttpClient _client;
+
+ public I18nTest(WebApplicationFactory<Startup> factory)
+ {
+ _testApp = new TestApplication(factory);
+ _client = _testApp.Factory.CreateDefaultClient();
+ }
+
+ public void Dispose()
+ {
+ _client.Dispose();
+ _testApp.Dispose();
+ }
+
+ private const string DirectUrl = "testing/i18n/direct";
+ private const string LocalizerUrl = "testing/i18n/localizer";
+
+ [Theory]
+ [InlineData(DirectUrl)]
+ [InlineData(LocalizerUrl)]
+ public async Task DefaultShouldReturnEnglish(string url)
+ {
+ (await _client.GetStringAsync(url)).Should().ContainEquivalentOf("English");
+ }
+
+ [Theory]
+ [InlineData(DirectUrl, "en", true)]
+ [InlineData(LocalizerUrl, "en", true)]
+ [InlineData(DirectUrl, "en-US", true)]
+ [InlineData(LocalizerUrl, "en-US", true)]
+ [InlineData(DirectUrl, "zh", false)]
+ [InlineData(LocalizerUrl, "zh", false)]
+ public async Task ShouldWork(string url, string acceptLanguage, bool english)
+ {
+ var request = new HttpRequestMessage
+ {
+ Method = HttpMethod.Get,
+ RequestUri = new Uri(url, UriKind.RelativeOrAbsolute)
+ };
+ request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(acceptLanguage));
+ var body = await (await _client.SendAsync(request)).Content.ReadAsStringAsync();
+ body.Should().ContainEquivalentOf(english ? "English" : "中文");
+ request.Dispose();
+ }
+ }
+}
diff --git a/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs b/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs deleted file mode 100644 index 4084dd8f..00000000 --- a/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.Localization;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-
-namespace Timeline.Tests.Mock.Services
-{
- internal static class TestStringLocalizerFactory
- {
- internal static IStringLocalizerFactory Create()
- {
- return new ResourceManagerStringLocalizerFactory(
- Options.Create(new LocalizationOptions()
- {
- ResourcesPath = "Resource"
- }),
- NullLoggerFactory.Instance
- );
- }
-
- internal static IStringLocalizer<T> Create<T>(this IStringLocalizerFactory factory)
- {
- return new StringLocalizer<T>(factory);
- }
- }
-}
diff --git a/Timeline.Tests/UsernameValidatorUnitTest.cs b/Timeline.Tests/UsernameValidatorUnitTest.cs index 283e18e2..d02367be 100644 --- a/Timeline.Tests/UsernameValidatorUnitTest.cs +++ b/Timeline.Tests/UsernameValidatorUnitTest.cs @@ -4,6 +4,7 @@ using Xunit; namespace Timeline.Tests
{
+ [UseCulture("en")]
public class UsernameValidatorUnitTest : IClassFixture<UsernameValidator>
{
private readonly UsernameValidator _validator;
@@ -15,9 +16,9 @@ namespace Timeline.Tests private string FailAndMessage(string username)
{
- var (result, messageGenerator) = _validator.Validate(username);
+ var (result, message) = _validator.Validate(username);
result.Should().BeFalse();
- return messageGenerator(null);
+ return message;
}
[Fact]
@@ -29,9 +30,8 @@ namespace Timeline.Tests [Fact]
public void NotString()
{
- var (result, messageGenerator) = _validator.Validate(123);
+ var (result, message) = _validator.Validate(123);
result.Should().BeFalse();
- var message = messageGenerator(null);
message.Should().ContainEquivalentOf("type");
}
|