diff options
author | 杨宇千 <crupest@outlook.com> | 2019-10-21 13:41:46 +0800 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-10-21 13:41:46 +0800 |
commit | ea8dd31e88aaf13af1f51e764623d6a7c73fb429 (patch) | |
tree | 705580fda162d3a2667f288ddb8092e8fba09703 /Timeline.Tests | |
parent | 1ce632ad3c093bd60e4c75dd4229266604012729 (diff) | |
download | timeline-ea8dd31e88aaf13af1f51e764623d6a7c73fb429.tar.gz timeline-ea8dd31e88aaf13af1f51e764623d6a7c73fb429.tar.bz2 timeline-ea8dd31e88aaf13af1f51e764623d6a7c73fb429.zip |
...
Diffstat (limited to 'Timeline.Tests')
-rw-r--r-- | Timeline.Tests/Controllers/TokenControllerTest.cs | 13 | ||||
-rw-r--r-- | Timeline.Tests/Controllers/UserControllerTest.cs | 109 | ||||
-rw-r--r-- | Timeline.Tests/GlobalSuppressions.cs | 5 | ||||
-rw-r--r-- | Timeline.Tests/Helpers/AssertionResponseExtensions.cs | 46 | ||||
-rw-r--r-- | Timeline.Tests/Helpers/HttpClientExtensions.cs | 13 | ||||
-rw-r--r-- | Timeline.Tests/Helpers/ImageHelper.cs | 24 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/UserUnitTest.cs | 22 | ||||
-rw-r--r-- | Timeline.Tests/Mock/Services/MockStringLocalizer.cs | 31 | ||||
-rw-r--r-- | Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs | 25 |
9 files changed, 194 insertions, 94 deletions
diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs index 86a241e5..71520e77 100644 --- a/Timeline.Tests/Controllers/TokenControllerTest.cs +++ b/Timeline.Tests/Controllers/TokenControllerTest.cs @@ -20,13 +20,14 @@ namespace Timeline.Tests.Controllers private readonly Mock<IUserService> _mockUserService = new Mock<IUserService>();
private readonly TestClock _mockClock = new TestClock();
+
private readonly TokenController _controller;
public TokenControllerTest()
{
_controller = new TokenController(_mockUserService.Object,
NullLogger<TokenController>.Instance, _mockClock,
- new MockStringLocalizer<TokenController>());
+ TestStringLocalizerFactory.Create().Create<TokenController>());
}
public void Dispose()
@@ -53,7 +54,7 @@ namespace Timeline.Tests.Controllers Password = "p",
Expire = expire
});
- action.Should().BeAssignableTo<OkObjectResult>()
+ action.Result.Should().BeAssignableTo<OkObjectResult>()
.Which.Value.Should().BeEquivalentTo(createResult);
}
@@ -67,7 +68,7 @@ namespace Timeline.Tests.Controllers Password = "p",
Expire = null
});
- action.Should().BeAssignableTo<BadRequestObjectResult>()
+ action.Result.Should().BeAssignableTo<BadRequestObjectResult>()
.Which.Value.Should().BeAssignableTo<CommonResponse>()
.Which.Code.Should().Be(Create.BadCredential);
}
@@ -82,7 +83,7 @@ namespace Timeline.Tests.Controllers Password = "p",
Expire = null
});
- action.Should().BeAssignableTo<BadRequestObjectResult>()
+ action.Result.Should().BeAssignableTo<BadRequestObjectResult>()
.Which.Value.Should().BeAssignableTo<CommonResponse>()
.Which.Code.Should().Be(Create.BadCredential);
}
@@ -93,7 +94,7 @@ namespace Timeline.Tests.Controllers const string token = "aaaaaaaaaaaaaa";
_mockUserService.Setup(s => s.VerifyToken(token)).ReturnsAsync(MockUser.User.Info);
var action = await _controller.Verify(new VerifyTokenRequest { Token = token });
- action.Should().BeAssignableTo<OkObjectResult>()
+ action.Result.Should().BeAssignableTo<OkObjectResult>()
.Which.Value.Should().BeAssignableTo<VerifyTokenResponse>()
.Which.User.Should().BeEquivalentTo(MockUser.User.Info);
}
@@ -113,7 +114,7 @@ namespace Timeline.Tests.Controllers const string token = "aaaaaaaaaaaaaa";
_mockUserService.Setup(s => s.VerifyToken(token)).ThrowsAsync(e);
var action = await _controller.Verify(new VerifyTokenRequest { Token = token });
- action.Should().BeAssignableTo<BadRequestObjectResult>()
+ action.Result.Should().BeAssignableTo<BadRequestObjectResult>()
.Which.Value.Should().BeAssignableTo<CommonResponse>()
.Which.Code.Should().Be(code);
}
diff --git a/Timeline.Tests/Controllers/UserControllerTest.cs b/Timeline.Tests/Controllers/UserControllerTest.cs new file mode 100644 index 00000000..9fec477f --- /dev/null +++ b/Timeline.Tests/Controllers/UserControllerTest.cs @@ -0,0 +1,109 @@ +using FluentAssertions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging.Abstractions;
+using Moq;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Controllers;
+using Timeline.Models;
+using Timeline.Models.Http;
+using Timeline.Services;
+using Timeline.Tests.Helpers;
+using Timeline.Tests.Mock.Data;
+using Timeline.Tests.Mock.Services;
+using Xunit;
+using static Timeline.ErrorCodes.Http.User;
+
+namespace Timeline.Tests.Controllers
+{
+ public class UserControllerTest : IDisposable
+ {
+ private readonly Mock<IUserService> _mockUserService = new Mock<IUserService>();
+
+ private readonly UserController _controller;
+
+ public UserControllerTest()
+ {
+ _controller = new UserController(NullLogger<UserController>.Instance,
+ _mockUserService.Object,
+ TestStringLocalizerFactory.Create());
+ }
+
+ public void Dispose()
+ {
+ _controller.Dispose();
+ }
+
+ [Fact]
+ public async Task GetList_Success()
+ {
+ var array = MockUser.UserInfoList.ToArray();
+ _mockUserService.Setup(s => s.ListUsers()).ReturnsAsync(array);
+ var action = await _controller.List();
+ action.Result.Should().BeAssignableTo<OkObjectResult>()
+ .Which.Value.Should().BeEquivalentTo(array);
+ }
+
+ [Fact]
+ public async Task Get_Success()
+ {
+ const string username = "aaa";
+ _mockUserService.Setup(s => s.GetUser(username)).ReturnsAsync(MockUser.User.Info);
+ var action = await _controller.Get(username);
+ action.Result.Should().BeAssignableTo<OkObjectResult>()
+ .Which.Value.Should().BeEquivalentTo(MockUser.User.Info);
+ }
+
+ [Fact]
+ public async Task Get_NotFound()
+ {
+ const string username = "aaa";
+ _mockUserService.Setup(s => s.GetUser(username)).Returns(Task.FromResult<UserInfo>(null));
+ var action = await _controller.Get(username);
+ action.Result.Should().BeAssignableTo<NotFoundObjectResult>()
+ .Which.Value.Should().BeAssignableTo<CommonResponse>()
+ .Which.Code.Should().Be(Get.NotExist);
+ }
+
+ [Theory]
+ [InlineData(PutResult.Created, true)]
+ [InlineData(PutResult.Modified, false)]
+ public async Task Put_Success(PutResult result, bool create)
+ {
+ const string username = "aaa";
+ const string password = "ppp";
+ const bool administrator = true;
+ _mockUserService.Setup(s => s.PutUser(username, password, administrator)).ReturnsAsync(result);
+ var action = await _controller.Put(new UserPutRequest
+ {
+ Password = password,
+ Administrator = administrator
+ }, username);
+ var response = action.Result.Should().BeAssignableTo<ObjectResult>()
+ .Which.Value.Should().BeAssignableTo<CommonPutResponse>()
+ .Which;
+ response.Code.Should().Be(0);
+ response.Data.Create.Should().Be(create);
+ }
+
+ [Fact]
+ public async Task Put_BadUsername()
+ {
+ const string username = "aaa";
+ const string password = "ppp";
+ const bool administrator = true;
+ _mockUserService.Setup(s => s.PutUser(username, password, administrator)).ThrowsAsync(new UsernameBadFormatException());
+ var action = await _controller.Put(new UserPutRequest
+ {
+ Password = password,
+ Administrator = administrator
+ }, username);
+ action.Result.Should().BeAssignableTo<BadRequestObjectResult>()
+ .Which.Value.Should().BeAssignableTo<CommonResponse>()
+ .Which.Code.Should().Be(Put.BadUsername);
+ }
+
+ //TODO! Complete this.
+ }
+}
diff --git a/Timeline.Tests/GlobalSuppressions.cs b/Timeline.Tests/GlobalSuppressions.cs index 6562efbb..2191a5c4 100644 --- a/Timeline.Tests/GlobalSuppressions.cs +++ b/Timeline.Tests/GlobalSuppressions.cs @@ -5,5 +5,10 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "This is not a UI application.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Tests name have underscores.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Test classes can be nested.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "This is redundant.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "Test classes do not need to implement it that way.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "Test classes do not need to implement it that way.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2234:Pass system uri objects instead of strings", Justification = "I really don't understand this rule.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Tests do not need make strings resources.")]
+
diff --git a/Timeline.Tests/Helpers/AssertionResponseExtensions.cs b/Timeline.Tests/Helpers/AssertionResponseExtensions.cs index 5ce025ee..08f10b2b 100644 --- a/Timeline.Tests/Helpers/AssertionResponseExtensions.cs +++ b/Timeline.Tests/Helpers/AssertionResponseExtensions.cs @@ -82,22 +82,14 @@ namespace Timeline.Tests.Helpers {
body = Subject.Content.ReadAsStringAsync().Result;
}
- catch (Exception e)
+ catch (AggregateException e)
{
- a.FailWith("Expected response body of {context:HttpResponseMessage} to be json string{reason}, but failed to read it or it was not a string. Exception is {0}.", e);
+ a.FailWith("Expected response body of {context:HttpResponseMessage} to be json string{reason}, but failed to read it or it was not a string. Exception is {0}.", e.InnerExceptions);
return new AndWhichConstraint<HttpResponseMessage, T>(Subject, null);
}
- try
- {
- var result = JsonConvert.DeserializeObject<T>(body);
- return new AndWhichConstraint<HttpResponseMessage, T>(Subject, result);
- }
- catch (Exception e)
- {
- a.FailWith("Expected response body of {context:HttpResponseMessage} to be able to convert to {0} instance{reason}, but failed. Exception is {1}.", typeof(T).FullName, e);
- return new AndWhichConstraint<HttpResponseMessage, T>(Subject, null);
- }
+ var result = JsonConvert.DeserializeObject<T>(body);
+ return new AndWhichConstraint<HttpResponseMessage, T>(Subject, result);
}
}
@@ -118,28 +110,22 @@ namespace Timeline.Tests.Helpers return assertions.HaveJsonBody<CommonDataResponse<TData>>(because, becauseArgs);
}
- public static void BePutCreate(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- assertions.HaveStatusCode(201, because, becauseArgs)
- .And.Should().HaveCommonDataBody<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Create(), because, becauseArgs);
- }
-
- public static void BePutModify(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- assertions.HaveStatusCode(200, because, becauseArgs)
- .And.Should().HaveCommonDataBody<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Modify(), because, becauseArgs);
- }
-
- public static void BeDeleteDelete(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
+ public static void BePut(this HttpResponseMessageAssertions assertions, bool create, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCode(200, because, becauseArgs)
- .And.Should().HaveCommonDataBody<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.Delete(), because, becauseArgs);
+ var body = assertions.HaveStatusCode(create ? 201 : 200, because, becauseArgs)
+ .And.Should().HaveJsonBody<CommonPutResponse>(because, becauseArgs)
+ .Which;
+ body.Code.Should().Be(0);
+ body.Data.Create.Should().Be(create);
}
- public static void BeDeleteNotExist(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
+ public static void BeDelete(this HttpResponseMessageAssertions assertions, bool delete, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCode(200, because, becauseArgs)
- .And.Should().HaveCommonDataBody<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.NotExist(), because, becauseArgs);
+ var body = assertions.HaveStatusCode(200, because, becauseArgs)
+ .And.Should().HaveJsonBody<CommonDeleteResponse>(because, becauseArgs)
+ .Which;
+ body.Code.Should().Be(0);
+ body.Data.Delete.Should().Be(delete);
}
public static void BeInvalidModel(this HttpResponseMessageAssertions assertions, string message = null)
diff --git a/Timeline.Tests/Helpers/HttpClientExtensions.cs b/Timeline.Tests/Helpers/HttpClientExtensions.cs index e3beea1d..38641f90 100644 --- a/Timeline.Tests/Helpers/HttpClientExtensions.cs +++ b/Timeline.Tests/Helpers/HttpClientExtensions.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json;
+using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
@@ -11,12 +12,24 @@ namespace Timeline.Tests.Helpers {
public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient client, string url, T body)
{
+ return client.PatchAsJsonAsync(new Uri(url, UriKind.RelativeOrAbsolute), body);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
+ public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient client, Uri url, T body)
+ {
return client.PatchAsync(url, new StringContent(
JsonConvert.SerializeObject(body), Encoding.UTF8, MediaTypeNames.Application.Json));
}
public static Task<HttpResponseMessage> PutByteArrayAsync(this HttpClient client, string url, byte[] body, string mimeType)
{
+ return client.PutByteArrayAsync(new Uri(url, UriKind.RelativeOrAbsolute), body, mimeType);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
+ public static Task<HttpResponseMessage> PutByteArrayAsync(this HttpClient client, Uri url, byte[] body, string mimeType)
+ {
var content = new ByteArrayContent(body);
content.Headers.ContentLength = body.Length;
content.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
diff --git a/Timeline.Tests/Helpers/ImageHelper.cs b/Timeline.Tests/Helpers/ImageHelper.cs index 2a2f3870..9bed0917 100644 --- a/Timeline.Tests/Helpers/ImageHelper.cs +++ b/Timeline.Tests/Helpers/ImageHelper.cs @@ -9,26 +9,18 @@ namespace Timeline.Tests.Helpers {
public static byte[] CreatePngWithSize(int width, int height)
{
- using (var image = new Image<Rgba32>(width, height))
- {
- using (var stream = new MemoryStream())
- {
- image.SaveAsPng(stream);
- return stream.ToArray();
- }
- }
+ using var image = new Image<Rgba32>(width, height);
+ using var stream = new MemoryStream();
+ image.SaveAsPng(stream);
+ return stream.ToArray();
}
public static byte[] CreateImageWithSize(int width, int height, IImageFormat format)
{
- using (var image = new Image<Rgba32>(width, height))
- {
- using (var stream = new MemoryStream())
- {
- image.Save(stream, format);
- return stream.ToArray();
- }
- }
+ using var image = new Image<Rgba32>(width, height);
+ using var stream = new MemoryStream();
+ image.Save(stream, format);
+ return stream.ToArray();
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserUnitTest.cs b/Timeline.Tests/IntegratedTests/UserUnitTest.cs index b2aab24c..47a8699c 100644 --- a/Timeline.Tests/IntegratedTests/UserUnitTest.cs +++ b/Timeline.Tests/IntegratedTests/UserUnitTest.cs @@ -4,13 +4,13 @@ using System; using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
-using Timeline.Controllers;
using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Tests.Helpers;
using Timeline.Tests.Helpers.Authentication;
using Timeline.Tests.Mock.Data;
using Xunit;
+using static Timeline.ErrorCodes.Http.User;
namespace Timeline.Tests.IntegratedTests
{
@@ -57,7 +57,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("users/usernotexist");
res.Should().HaveStatusCode(404)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.Get_NotExist);
+ .Which.Code.Should().Be(Get.NotExist);
}
public static IEnumerable<object[]> Put_InvalidModel_Data()
@@ -88,7 +88,7 @@ namespace Timeline.Tests.IntegratedTests });
res.Should().HaveStatusCode(400)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.Put_BadUsername);
+ .Which.Code.Should().Be(Put.BadUsername);
}
private async Task CheckAdministrator(HttpClient client, string username, bool administrator)
@@ -108,7 +108,7 @@ namespace Timeline.Tests.IntegratedTests Password = "password",
Administrator = false
});
- res.Should().BePutModify();
+ res.Should().BePut(false);
await CheckAdministrator(client, MockUser.User.Username, false);
}
@@ -124,7 +124,7 @@ namespace Timeline.Tests.IntegratedTests Password = "password",
Administrator = false
});
- res.Should().BePutCreate();
+ res.Should().BePut(true);
await CheckAdministrator(client, username, false);
}
@@ -135,7 +135,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.PatchAsJsonAsync("users/usernotexist", new UserPatchRequest { });
res.Should().HaveStatusCode(404)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.Patch_NotExist);
+ .Which.Code.Should().Be(Patch.NotExist);
}
[Fact]
@@ -156,7 +156,7 @@ namespace Timeline.Tests.IntegratedTests using var client = await _factory.CreateClientAsAdmin();
var url = "users/" + MockUser.User.Username;
var res = await client.DeleteAsync(url);
- res.Should().BeDeleteDelete();
+ res.Should().BeDelete(true);
var res2 = await client.GetAsync(url);
res2.Should().HaveStatusCode(404);
@@ -167,7 +167,7 @@ namespace Timeline.Tests.IntegratedTests {
using var client = await _factory.CreateClientAsAdmin();
var res = await client.DeleteAsync("users/usernotexist");
- res.Should().BeDeleteNotExist();
+ res.Should().BeDelete(false);
}
@@ -214,7 +214,7 @@ namespace Timeline.Tests.IntegratedTests new ChangeUsernameRequest { OldUsername = "usernotexist", NewUsername = "newUsername" });
res.Should().HaveStatusCode(400)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.ChangeUsername_NotExist);
+ .Which.Code.Should().Be(Op.ChangeUsername.NotExist);
}
[Fact]
@@ -225,7 +225,7 @@ namespace Timeline.Tests.IntegratedTests new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = MockUser.Admin.Username });
res.Should().HaveStatusCode(400)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.ChangeUsername_AlreadyExist);
+ .Which.Code.Should().Be(Op.ChangeUsername.AlreadyExist);
}
[Fact]
@@ -282,7 +282,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.PostAsJsonAsync(url, new ChangePasswordRequest { OldPassword = "???", NewPassword = "???" });
res.Should().HaveStatusCode(400)
.And.Should().HaveCommonBody()
- .Which.Code.Should().Be(UserController.ErrorCodes.ChangePassword_BadOldPassword);
+ .Which.Code.Should().Be(Op.ChangePassword.BadOldPassword);
}
[Fact]
diff --git a/Timeline.Tests/Mock/Services/MockStringLocalizer.cs b/Timeline.Tests/Mock/Services/MockStringLocalizer.cs deleted file mode 100644 index 7729d56c..00000000 --- a/Timeline.Tests/Mock/Services/MockStringLocalizer.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.Extensions.Localization;
-using System.Collections.Generic;
-using System.Globalization;
-
-namespace Timeline.Tests.Mock.Services
-{
- public class MockStringLocalizer : IStringLocalizer
- {
- private const string mockKey = "MOCK_KEY";
- private const string mockString = "THIS IS A MOCK LOCALIZED STRING.";
-
- public LocalizedString this[string name] => new LocalizedString(name, mockString);
-
- public LocalizedString this[string name, params object[] arguments] => new LocalizedString(name, mockString);
-
- public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
- {
- yield return new LocalizedString(mockKey, mockString);
- }
-
- public IStringLocalizer WithCulture(CultureInfo culture)
- {
- return this;
- }
- }
-
- public class MockStringLocalizer<T> : MockStringLocalizer, IStringLocalizer<T>
- {
-
- }
-}
diff --git a/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs b/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs new file mode 100644 index 00000000..4084dd8f --- /dev/null +++ b/Timeline.Tests/Mock/Services/TestStringLocalizerFactory.cs @@ -0,0 +1,25 @@ +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);
+ }
+ }
+}
|