├── .github └── workflows │ ├── build_test.yml │ └── deploy_nuget.yml ├── README.md ├── asset └── wd3w.png ├── build └── version.props ├── docs └── images │ └── easy-testing-logo.png ├── script ├── .gitignore └── package.json └── src ├── .gitignore ├── Directory.Build.props ├── Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore ├── InMemoryDbContextHelper.cs ├── SqliteInMemoryDbContextHelper.cs └── Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore.csproj ├── Wd3w.AspNetCore.EasyTesting.FakeItEasy ├── SystemUnderTestFakeItEasyExtensions.cs └── Wd3w.AspNetCore.EasyTesting.FakeItEasy.csproj ├── Wd3w.AspNetCore.EasyTesting.Grpc ├── GrpcClientHelper.cs ├── ResponseVersionHandler.cs └── Wd3w.AspNetCore.EasyTesting.Grpc.csproj ├── Wd3w.AspNetCore.EasyTesting.Hestify ├── HestifyExtensionHelper.cs └── Wd3w.AspNetCore.EasyTesting.Hestify.csproj ├── Wd3w.AspNetCore.EasyTesting.Moq ├── SystemUnderTestMoqExtensions.cs └── Wd3w.AspNetCore.EasyTesting.Moq.csproj ├── Wd3w.AspNetCore.EasyTesting.NSubstitute ├── SystemUnderTestNSubstituteExtensions.cs └── Wd3w.AspNetCore.EasyTesting.NSubstitute.csproj ├── Wd3w.AspNetCore.EasyTesting.SampleApi ├── Controllers │ └── SampleController.cs ├── Entities │ └── SampleDb.cs ├── Models │ └── SampleDataResponse.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Services │ ├── CustomTokenAuthService.cs │ ├── ISampleService.cs │ ├── SampleRepository.cs │ └── SampleService.cs ├── Startup.cs ├── Wd3w.AspNetCore.EasyTesting.SampleApi.csproj ├── appsettings.Development.json └── appsettings.json ├── Wd3w.AspNetCore.EasyTesting.Test ├── Authentication │ ├── FakeAuthenticationTest.cs │ └── NoUserAuthenticationTest.cs ├── Common │ ├── EasyTestingTestBase.cs │ ├── FakeSampleService.cs │ ├── INeverRegisteredServiceType.cs │ └── TestApiFactory.cs ├── Helper │ └── HestifyClientHelper.cs ├── HestifyExtension │ └── HestifyExtensionHelperTest.cs ├── InMemoryDbContext │ ├── InMemoryDbContextHelperTest.cs │ └── SqliteInMemoryDbContextHelperTest.cs ├── MockSupports │ ├── FakeItEasyExtensionsTest.cs │ ├── MoqExtensionsTest.cs │ └── NSubstituteExtensionsTest.cs ├── SampleTest.cs ├── SystemUnderTest │ ├── ConfigureAppConfigurationTest.cs │ ├── ConfigureServicesTest.cs │ ├── CreateClientTest.cs │ ├── DisableOptionDataAnnotationValidationTest.cs │ ├── DisableStartupFiltersTest.cs │ ├── GetOrAddInternalServiceTest.cs │ ├── RemoveAllByTest.cs │ ├── RemoveAllTest.cs │ ├── RemoveSingleByTest.cs │ ├── RemoveTest.cs │ ├── ReplaceConfigureOptionsTest.cs │ ├── ReplaceDistributedInMemoryCacheTest.cs │ ├── ReplaceLoggerFactoryTest.cs │ ├── ReplaceServiceTest.cs │ ├── SetupFixtureTest.cs │ ├── SetupWebHostBuilderTest.cs │ ├── UseEnvironmentTest.cs │ ├── UseSettingTest.cs │ ├── UsingServiceAsyncTest.cs │ ├── UsingServiceTest.cs │ ├── VerifyNoRegistrationByConditionTest.cs │ ├── VerifyNoRegistrationTest.cs │ ├── VerifyRegisteredLifeTimeOfServiceTest.cs │ └── VerifyRegistrationByConditionTest.cs └── Wd3w.AspNetCore.EasyTesting.Test.csproj ├── Wd3w.AspNetCore.EasyTesting.sln ├── Wd3w.AspNetCore.EasyTesting.sln.DotSettings └── Wd3w.AspNetCore.EasyTesting ├── Authentication ├── AuthenticationHandlerHelper.cs └── FakeAuthenticationHandler.cs ├── HttpResponseMessageAssertionHelper.cs ├── Internal └── ServiceCollectionHelper.cs ├── SystemUnderTest.Verify.cs ├── SystemUnderTest.cs ├── SystemUnderTestBase.cs └── Wd3w.AspNetCore.EasyTesting.csproj /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | name: Build And Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: 5.0.x 18 | - name: Install dependencies 19 | run: dotnet restore 20 | working-directory: src 21 | - name: Build 22 | run: dotnet build --configuration Release --no-restore 23 | working-directory: src/Wd3w.AspNetCore.EasyTesting.Test 24 | - name: Test 25 | run: dotnet test --no-restore --verbosity normal 26 | working-directory: src/Wd3w.AspNetCore.EasyTesting.Test 27 | -------------------------------------------------------------------------------- /.github/workflows/deploy_nuget.yml: -------------------------------------------------------------------------------- 1 | name: Deploy nuget 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - build/version.props 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: 5.0.x 18 | - name: Install dependencies 19 | run: dotnet restore 20 | working-directory: src 21 | - name: Build solution 22 | run: dotnet build --no-restore 23 | working-directory: src 24 | - name: Deploy nuget package to nuget 25 | run: dotnet nuget push *\bin\**\*.nupkg -k ${{ secrets.WD3W_NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate 26 | working-directory: src 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AspNetCore.EasyTesting 2 | 3 | > Test library for integration test make easy and this library is fully tested! 4 | 5 | ![banner](docs/images/easy-testing-logo.png) 6 | 7 | ![Build And Test](https://github.com/WDWWW/aspnetcore-easy-testing/workflows/Build%20And%20Test/badge.svg) 8 | ![Nuget](https://img.shields.io/nuget/v/AspNetCore.EasyTesting) 9 | ![Nuget](https://img.shields.io/nuget/dt/AspNetCore.EasyTesting) 10 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/y/wdwww/aspnetcore-easy-testing) 11 | 12 | ## Features 13 | - Replace to memory cache for `IDistributedCache` 14 | - Replace to InMemoryDb Easily for your DbContext 15 | - Replace internal service to your own object. 16 | - Change ASP.NET Core host environment value(like `Development`, `Production`) 17 | - Mock/Fake internal service using by Moq, FakeItEasy, NSubstitute 18 | - Provide build api for writing testing code declarative. 19 | - Getting/Using service after init the test service. 20 | - Provide hooks like `SetupFixture`, `Configure*` Methods. 21 | - Fake Authentication!! 22 | - Verify your service registration for your conditional registration logic. :satisfied: 23 | 24 | ## Getting Start! 25 | 26 | First of all, you have to install this package like below in your project. 27 | 28 | ```bash 29 | dotnet add packge AspNetCore.EasyTesting 30 | ``` 31 | 32 | And you can create SUT(Short word of System Under Test) object of your Startup class. 33 | 34 | ```csharp 35 | using var sut = new SystemUnderTest(); // That's all! 36 | ``` 37 | 38 | If you want to replace your own services to fake service and check response data. 39 | Write code like below. 40 | 41 | ```csharp 42 | sut.Replace(new FakeService()); 43 | 44 | var client = sut.CreateHttpClient(); 45 | 46 | var response = await client.GetAsync("path/to/your/api"); 47 | 48 | resopnse.ShouldBeOk(apiResponse => { 49 | apiResponse.Field.Should().NotBeEmpty(); 50 | }); 51 | ``` 52 | 53 | And this framework is not depends on ANY test framework. What you want, you can use this library for writing integration test easily! :') 54 | 55 | ### Replace DbContext to In-Memory DbContext. 56 | 57 | If you want to change your DbContext connection with in-memory db-context? You don't need to separate environment in your api project! 58 | Just add package and call `ReplaceInMemoryDbContext` or `ReplaceSqliteInMemoryDbContext`! 59 | 60 | ```sh 61 | dotnet add package AspNetCore.EasyTesting.EntityFrameworkCore 62 | ``` 63 | 64 | ```cs 65 | sut.ReplaceInMemoryDbContext() // Replace your registered both `DbContextOptions` and `DbContextOptions` 66 | .SetupFixture(async db => 67 | { 68 | db.SampleDataEntities.Add(new SampleDataEntity()); 69 | await db.SaveChangesAsync(); 70 | }); 71 | ``` 72 | 73 | Note that if there are no any specific database name about `ReplaceInMemoryDbContext`, the database name is assigned `Guid.NewGuid().ToString()` 74 | to prevent fail test when parallel test run environment. 75 | 76 | ### Replace to use MemoryCache for IDistributed type. 77 | 78 | Use `ReplaceDistributedInMemoryCache` If you want to replace distributed cache with memory cache 79 | 80 | ```cs 81 | sut.ReplaceDistributedInMemoryCache() 82 | .CreateClient(); 83 | ``` 84 | 85 | 86 | 87 | ### Mock/Fake you owned services! 88 | 89 | If you want to replace your own service with mock/fake object. 90 | 91 | ```sh 92 | dotnet add package AspNetCore.EasyTesting.[Moq/FakeItEasy/NSubstitute] 93 | ``` 94 | 95 | Note that, how many you call replace fake/mock service method, this create library only once and re-use fake/mock object. that mean, you can write mocking chaining code elegantly. ;) 96 | 97 | #### Moq 98 | ```csharp 99 | // All cases is same! 100 | var mockService = sut.MockService(); 101 | sut.MockService(out var mock); 102 | sut.MockService(mock => mock 103 | .Setup(service => service.GetSampleDate()) 104 | .Returns("Action mocked!")) 105 | 106 | // Verify helper 107 | sut.VerifyCallOnce(service => service.GetSampleDate()); 108 | sut.VerifyCall(service => service.GetSampleDate(), Times.Once()); 109 | ``` 110 | 111 | #### FakeItEasy 112 | ```csharp 113 | // Dummy service 114 | sut.ReplaceDummyService(); 115 | 116 | // Replace fake service 117 | var fakeService = sut.FakeService(); 118 | sut.FakeService(out var fakeService); 119 | sut.FakeService(service => A 120 | .CallTo(() => service.GetSampleDate()) 121 | .Returns("MockedData")) 122 | 123 | // Take registered fake service 124 | var fakeService = sut.GetFakeService(); 125 | sut.UseFakeService(service => A.CallTo(() => service.GetSampleDate()).MustHaveHappend()); 126 | ``` 127 | 128 | #### NSubstitute 129 | ```csharp 130 | // Replace service with substitute 131 | sut.ReplaceWithSubstitute(out var mockedService); 132 | sut.ReplaceWithSubstitute(substitute => /* setup */); 133 | var substitute = sut.ReplaceWithSubstitute(); 134 | 135 | // Verify 136 | sut.GetSubstitute().Received().GetSampleDate(); 137 | sut.UseSubstitute(substitute => substitute.Received().GetSampleDate()); 138 | ``` 139 | 140 | ### Replace Pre-Registered application service with new service. 141 | 142 | ```csharp 143 | sut.ReplaceService(); 144 | sut.ReplaceService(new FakeSampleService()); // It will be always registered as singleton. 145 | ``` 146 | 147 | ### Use internal service object for verifying test is successfully working. 148 | 149 | Use `SystemUnderTest.UsingServiceAsync` or `SystemUnderTest.UsingService` methods for use internal service. 150 | 151 | ```csharp 152 | await sut.UsingServiceAsync(async service => { 153 | var updated = await service.GetSameplAsync(OriginalFixtureId); 154 | updated.Data.Should().Be("Newer Sample Data"); 155 | }); 156 | ``` 157 | 158 | 159 | ### Verify lifetime of service registration. 160 | 161 | Use `SystemUnderTest.VerifyRegisteredLifeTimeOfService` to verify to register your application service lifetime rightly. 162 | 163 | ```csharp 164 | var sut = new SystemUnderTest(); 165 | 166 | sut.VerifyRegisteredLifeTimeOfService(ServiceLifetime.Scoped); 167 | ``` 168 | 169 | ### Verify implementation type registration about service type 170 | 171 | Use `VerifyRegisteredImplementationTypeOfService` to verify implementation type registration for service type 172 | when you have conditional service registration logic on startup class. 173 | 174 | ```csharp 175 | // Given 176 | var sut = new SystemUnderTest(); 177 | sut.UseProductionEnvironment(); 178 | 179 | // When 180 | sut.CreateClient(); 181 | 182 | // Then 183 | sut.VerifyRegisteredImplementationTypeOfService(); 184 | ``` 185 | 186 | 187 | ### Change host environment value 188 | 189 | ``` cs 190 | var client = new SystemUnderTest() 191 | .UseProductionEnvironment() 192 | .CreateClient(); 193 | ``` 194 | 195 | - `UseEnvironment` 196 | - `UseDevelopmentEnvironment` 197 | - `UseStagingEnvironment` 198 | - `UseProductionEnvironment` 199 | 200 | ### Use delegate methods of IWebHostBuilder for customizing by your hand! :wave: 201 | 202 | SystemUnderTest provide delegate methods of IWebHostBuilder to allow fully customization of your way. Check out below methods. 203 | 204 | - `SystemUnderTest.ConfigureServices` 205 | - `SystemUnderTest.ConfigureAppConfiguration` 206 | - `SystemUnderTest.UseSetting` 207 | - `SystemUnderTest.SetupWebHostBuilder` 208 | 209 | 210 | ### Fake authentication layer of ASP.NET Core 211 | 212 | We can do fake authentication handler easily through call few method. 213 | But It's not recommended for custom complex implementation of authentication process. 214 | 215 | ```cs 216 | usnig var sut = new SystemUnderTest(); 217 | 218 | // this replace disable defualt original authentication handler with fake authentication handler 219 | var httpClient = sut.NoUserAuthentication() 220 | // .AllowAuthentication("Bearer", new ClaimsPrincipal(/* configure test user principal */)) for faking specific authentication scheme. 221 | // .DenyAuthentication() for test unauthorized call 222 | .CreateClient(); 223 | 224 | // Now you can call any authorized actions 225 | ``` 226 | 227 | This library provide you four type methods. 228 | - `NoUserAuthentication` : When server couldn't find authentication related parts(Cookie, Token, JWT ... etc) from http call. 229 | - `DenyAuthentication` : When server found authentication related parts But it's invalid 230 | - `AllowAuthentication` : When server found valid authentication related parts and can make `IPrincipal` 231 | - `FakeAuthentication` : What you want, just call this method and provide valid ticket or principal instance. 232 | 233 | If you don't provide `sheme` parameter, it will use `DefaultScheme` to fake that is already configured in `Startup.cs` by your hand. :wave: 234 | 235 | ```cs 236 | services.AddAuthentication("Bearer") // "Bearer" scheme is default authentication scheme. 237 | ``` 238 | 239 | Note that if you want to use your own `IIdentity` when fake authentication handler for some scheme. 240 | `IIdentity.IsAuthenticated` should return `true`. if it is not, every call is denied by ASP.NET Core authentication middleware. 241 | 242 | ### Support GRPC Client 243 | 244 | This is just simple grpc support for creating grpc client easily. 245 | 246 | ```sh 247 | dotnet add package AspNetCore.EasyTesting.Grpc 248 | ``` 249 | 250 | 251 | - `CreateGrpcChannel` : create grpc channel for creating grpc client. 252 | - `CreateGrpcClient` : `T` is generated grpc client by protoc. 253 | 254 | ```csharp 255 | usign var client = new SystemUnderTest() 256 | .UseProductionEnvironment() 257 | .CreateGrpcClient(); 258 | 259 | await client.DoSomethingAsync(new { Property = 1 }); 260 | ``` 261 | 262 | ### More methods. 263 | - `ReplaceConfigureOptions` : Replace registered configure options. if you used custom configure some option class in startup class. 264 | - `ReplaceNamedConfigureOptions` : Like above but just for named options. 265 | - `DisableOptionValidations` : Remove all options validators about `TOptions` 266 | - `DisableOptionDataAnnotationValidation` : Disable only data annotation validation. 267 | - `VerifyRegistrationByCondition` : Verify service registration by condition expression. 268 | - `OverrideAppConfiguration` overloads 269 | - `ReplaceLoggerFactory` : Replace logger factory to generating custom logger. 270 | - for example : [checkout XUnit test sample](src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ReplaceLoggerFactoryTest.cs) 271 | - `DisableStartupFilters`, `DisableStartupFilter` : Remove service registrations of `IStartFilter` 272 | - `RemoveSingleBy`, `RemoveAllBy`, `Remove`, `RemoveAll` : Remove service or implementation types. 273 | - `VerifyNoRegistrationByCondition`, `VerifyNoRegistration` : Verify there are no registration of condition or service type. 274 | 275 | ### And more use cases.. 276 | 277 | Visit this library [test project](src/Wd3w.AspNetCore.EasyTesting.Test). You can see every cases to use this library. 278 | 279 | ## LICENSE 280 | 281 | MIT License 282 | 283 | Copyright (c) 2021 WDWWW 284 | 285 | Permission is hereby granted, free of charge, to any person obtaining a copy 286 | of this software and associated documentation files (the "Software"), to deal 287 | in the Software without restriction, including without limitation the rights 288 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 289 | copies of the Software, and to permit persons to whom the Software is 290 | furnished to do so, subject to the following conditions: 291 | 292 | The above copyright notice and this permission notice shall be included in all 293 | copies or substantial portions of the Software. 294 | 295 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 296 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 297 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 298 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 299 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 300 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 301 | SOFTWARE. -------------------------------------------------------------------------------- /asset/wd3w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WDWWW/aspnetcore-easy-testing/d49857f87aaa20546e775825209c9d3f899c4cd6/asset/wd3w.png -------------------------------------------------------------------------------- /build/version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5.0.4 4 | 5 | -------------------------------------------------------------------------------- /docs/images/easy-testing-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WDWWW/aspnetcore-easy-testing/d49857f87aaa20546e775825209c9d3f899c4cd6/docs/images/easy-testing-logo.png -------------------------------------------------------------------------------- /script/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aspnetcore-easytesting", 3 | "version": "0.0.0", 4 | "description": "Release script for easytesting", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "release": { 10 | "plugins": [ 11 | ["@semantic-release/commit-analyzer", { 12 | "preset": "angular", 13 | "releaseRules": [ 14 | {"type": "docs", "scope":"README", "release": "patch"}, 15 | {"type": "refactor", "release": "patch"} 16 | ], 17 | "parserOpts": { 18 | "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES"] 19 | } 20 | }], 21 | "@semantic-release/release-notes-generator", 22 | "@semantic-release/github" 23 | ] 24 | }, 25 | "author": "", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@semantic-release/changelog": "^5.0.1", 29 | "@semantic-release/git": "^9.0.0", 30 | "semantic-release": "^17.0.7" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | .idea 353 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 9.0 6 | AspNetCore.EasyTesting 7 | https://raw.githubusercontent.com/WDWWW/aspnetcore-easy-testing/master/asset/wd3w.png 8 | Jinseoung Lee 9 | aspnetcore;aspnet;test;integration;integration-testing;e2e; 10 | true 11 | https://github.com/WDWWW/aspnetcore-easy-testing 12 | 13 | 14 | 15 | true 16 | snupkg 17 | 18 | 19 | 20 | 5.0.0 21 | 22 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore/InMemoryDbContextHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore 5 | { 6 | public static class InMemoryDbContextHelper 7 | { 8 | private static DbContextOptions CreateInMemoryDbContextOptions(string databaseName) 9 | where TDbContext : DbContext 10 | { 11 | return new DbContextOptionsBuilder() 12 | .UseInMemoryDatabase(databaseName) 13 | .Options; 14 | } 15 | 16 | private static DbContextOptions CreateInMemoryDbContextOptions(string databaseName) 17 | { 18 | return new DbContextOptionsBuilder() 19 | .UseInMemoryDatabase(databaseName) 20 | .Options; 21 | } 22 | 23 | /// 24 | /// Replace DbContextOptions and DbContextOptions to InMemoryDbContextOptions and InMemoryDbContextOptions 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static SystemUnderTest ReplaceInMemoryDbContext( 31 | this SystemUnderTest sut, string databaseName = default) 32 | where TDbContext : DbContext 33 | { 34 | databaseName ??= Guid.NewGuid().ToString(); 35 | return sut.ReplaceService(CreateInMemoryDbContextOptions(databaseName)) 36 | .ReplaceService(CreateInMemoryDbContextOptions(databaseName)) 37 | .SetupFixture(async context => 38 | { 39 | var database = context.Database; 40 | await database.EnsureDeletedAsync(); 41 | await database.EnsureCreatedAsync(); 42 | }); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore/SqliteInMemoryDbContextHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Data.Sqlite; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore 5 | { 6 | public static class SqliteInMemoryDbContextHelper 7 | { 8 | private static DbContextOptions CreateInMemoryDbContextOptions() 9 | where TDbContext : DbContext 10 | { 11 | return new DbContextOptionsBuilder() 12 | .UseSqlite(CreateInMemoryConnection()) 13 | .Options; 14 | } 15 | 16 | private static DbContextOptions CreateInMemoryDbContextOptions() 17 | { 18 | return new DbContextOptionsBuilder() 19 | .UseSqlite(CreateInMemoryConnection()) 20 | .Options; 21 | } 22 | 23 | private static SqliteConnection CreateInMemoryConnection() 24 | { 25 | var connection = new SqliteConnection("DataSource=:memory:"); 26 | connection.Open(); 27 | return connection; 28 | } 29 | 30 | /// 31 | /// Replace DbContextOptions for TDbContext to InMemory SqliteDbContextOptions 32 | /// 33 | /// System under test 34 | /// DbContext type 35 | /// 36 | public static SystemUnderTest ReplaceSqliteInMemoryDbContext( 37 | this SystemUnderTest sut) 38 | where TDbContext : DbContext 39 | { 40 | return sut.ReplaceService(CreateInMemoryDbContextOptions()) 41 | .ReplaceService(CreateInMemoryDbContextOptions()) 42 | .SetupFixture(async context => 43 | { 44 | var database = context.Database; 45 | await database.OpenConnectionAsync(); 46 | await database.EnsureDeletedAsync(); 47 | await database.EnsureCreatedAsync(); 48 | }); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore/Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageIdBase).EntityFrameworkCore 5 | bin\Debug\Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore.xml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.FakeItEasy/SystemUnderTestFakeItEasyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using FakeItEasy; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.FakeItEasy 7 | { 8 | /// 9 | /// FakeItEasy Extension for SystemUnderTest 10 | /// 11 | public static class SystemUnderTestFakeItEasyExtensions 12 | { 13 | /// 14 | /// Replace service with dummy object 15 | /// 16 | /// 17 | /// Service to fake 18 | /// 19 | public static SystemUnderTest ReplaceDummyService(this SystemUnderTest sut) where TService : class 20 | { 21 | sut.CheckClientIsNotCreated(nameof(FakeService)); 22 | sut.ReplaceService(sut.GetOrAddInternalService(A.Dummy)); 23 | return sut; 24 | } 25 | 26 | /// 27 | /// Replace service with fake. If you call this replace method multiple times, The fake is created only once. 28 | /// 29 | /// 30 | /// Service to fake 31 | /// 32 | public static TService FakeService(this SystemUnderTest sut) where TService : class 33 | { 34 | sut.CheckClientIsNotCreated(nameof(FakeService)); 35 | var fake = sut.GetOrAddInternalService(A.Fake); 36 | sut.ReplaceService(fake); 37 | return fake; 38 | } 39 | 40 | /// 41 | /// Replace service with fake. If you call this replace method multiple times, The fake is created only once. 42 | /// 43 | /// 44 | /// 45 | /// Service to fake 46 | /// 47 | public static SystemUnderTest FakeService(this SystemUnderTest sut, [NotNull] out TService fake) where TService : class 48 | { 49 | fake = sut.FakeService(); 50 | return sut; 51 | } 52 | 53 | /// 54 | /// Replace service with fake. If you call this replace method multiple times, The fake is created only once. 55 | /// 56 | /// 57 | /// 58 | /// Service to fake 59 | /// 60 | public static SystemUnderTest FakeService(this SystemUnderTest sut, [NotNull] Action configureFake) where TService : class 61 | { 62 | configureFake(sut.FakeService()); 63 | return sut; 64 | } 65 | 66 | /// 67 | /// Get pre-registered fake service object. 68 | /// 69 | /// 70 | /// Service to fake 71 | /// 72 | public static TService GetFakeService(this SystemUnderTest sut) where TService : class 73 | { 74 | return sut.InternalServiceProvider.GetService() ?? throw new InvalidOperationException("Replace first using FakeService before call GetFakeService."); 75 | } 76 | 77 | /// 78 | /// Use pre-registered fake service object. 79 | /// 80 | /// 81 | /// 82 | /// Service to fake 83 | /// 84 | public static SystemUnderTest UseFakeService(this SystemUnderTest sut, [NotNull] Action useFakeService) where TService : class 85 | { 86 | useFakeService(sut.GetFakeService()); 87 | return sut; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.FakeItEasy/Wd3w.AspNetCore.EasyTesting.FakeItEasy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageIdBase).FakeItEasy 5 | bin\Debug\Wd3w.AspNetCore.EasyTesting.FakeItEasy.xml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Grpc/GrpcClientHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Grpc.Net.Client; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Grpc 5 | { 6 | public static class GrpcClientHelper 7 | { 8 | /// 9 | /// Create grpc client for T 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static T CreateClient(this SystemUnderTest sut) 15 | { 16 | var channel = CreateGrpcChannel(sut); 17 | return (T) Activator.CreateInstance(typeof(T), channel); 18 | } 19 | 20 | /// 21 | /// Create grpc channel for creating grpc service client 22 | /// 23 | /// 24 | /// 25 | public static GrpcChannel CreateGrpcChannel(this SystemUnderTest sut) 26 | { 27 | var httpClient = sut.CreateDefaultClient(new ResponseVersionHandler()); 28 | 29 | return GrpcChannel.ForAddress(httpClient.BaseAddress, new GrpcChannelOptions 30 | { 31 | HttpClient = httpClient 32 | }); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Grpc/ResponseVersionHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Grpc 6 | { 7 | public class ResponseVersionHandler : DelegatingHandler 8 | { 9 | protected override async Task SendAsync( 10 | HttpRequestMessage request, 11 | CancellationToken cancellationToken) 12 | { 13 | var response = await base.SendAsync(request, cancellationToken); 14 | response.Version = request.Version; 15 | 16 | return response; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Grpc/Wd3w.AspNetCore.EasyTesting.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PackageIdBase).Grpc 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Hestify/HestifyExtensionHelper.cs: -------------------------------------------------------------------------------- 1 | using Hestify; 2 | 3 | namespace Wd3w.AspNetCore.EasyTesting.Hestify 4 | { 5 | public static class HestifyExtensionHelper 6 | { 7 | public static HestifyClient Resource(this SystemUnderTest sut, string relativeUri) 8 | { 9 | return new HestifyClient(sut.CreateClient(), relativeUri); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Hestify/Wd3w.AspNetCore.EasyTesting.Hestify.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageIdBase).Hestify 5 | bin\Debug\Wd3w.AspNetCore.EasyTesting.Hestify.xml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Moq/SystemUnderTestMoqExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Moq; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.Moq 7 | { 8 | /// 9 | /// Mock extension helper for SystemUnderTestBase 10 | /// 11 | public static class SystemUnderTestMoqExtensions 12 | { 13 | /// 14 | /// Replace TService with Mock.Object on ASP.NET Core internal service. If you call this method multiple time, 15 | /// It's only replace the service once. 16 | /// 17 | /// 18 | /// Service type to mock 19 | /// 20 | public static Mock MockService(this SystemUnderTest sut) where TService : class 21 | { 22 | sut.CheckClientIsNotCreated(nameof(MockService)); 23 | var mock = sut.GetOrAddInternalService(() => new Mock()); 24 | sut.ReplaceService(mock.Object); 25 | return mock; 26 | } 27 | 28 | /// 29 | /// Replace TService with Mock.Object on ASP.NET Core internal service. If you call this method multiple time, 30 | /// It's only replace the service once. 31 | /// 32 | /// 33 | /// mock object 34 | /// Service type to mock 35 | /// 36 | public static SystemUnderTest MockService(this SystemUnderTest sut, out Mock mock) where TService : class 37 | { 38 | mock = sut.MockService(); 39 | return sut; 40 | } 41 | 42 | /// 43 | /// Replace TService with Mock.Object on ASP.NET Core internal service. If you call this method multiple time, 44 | /// It's only replace the service once. 45 | /// 46 | /// 47 | /// action to configure mock object 48 | /// Service type to mock 49 | /// 50 | public static SystemUnderTest MockService(this SystemUnderTest sut, Action> mockAction) where TService : class 51 | { 52 | mockAction(sut.MockService()); 53 | return sut; 54 | } 55 | 56 | /// 57 | /// Get service mock object from internal service collection. You should mock service before call this method. 58 | /// 59 | /// 60 | /// Mocked service type 61 | /// 62 | public static Mock GetServiceMock(this SystemUnderTest sut) where TService : class 63 | { 64 | return sut.InternalServiceProvider.GetService>() 65 | ?? throw new InvalidOperationException("Replace service to mock first using ReplaceWithNSubstitute before call this method."); 66 | } 67 | 68 | /// 69 | /// Get service mock object from internal service collection. You should mock service before call this method. 70 | /// 71 | /// 72 | /// mock object 73 | /// Mocked service type 74 | /// 75 | public static SystemUnderTest GetServiceMock(this SystemUnderTest sut, out Mock mock) 76 | where TService : class 77 | { 78 | mock = sut.GetServiceMock(); 79 | return sut; 80 | } 81 | 82 | /// 83 | /// Use service mock object from internal service collection. You should mock service before call this method. 84 | /// 85 | /// 86 | /// use mock 87 | /// Mocked service type 88 | /// 89 | public static SystemUnderTest UseServiceMock(this SystemUnderTest sut, Action> useMock) 90 | where TService : class 91 | { 92 | useMock(sut.GetServiceMock()); 93 | return sut; 94 | } 95 | 96 | /// 97 | /// Verify mock service short method. 98 | /// 99 | /// 100 | /// Expression for verify 101 | /// Times for verifying. Default value is AtLeastOnce if the times is not provided. 102 | /// Mocked service type 103 | /// 104 | public static SystemUnderTest VerifyCall(this SystemUnderTest sut, 105 | Expression> expression, 106 | Times? times = null) 107 | where TService : class 108 | { 109 | sut.UseServiceMock(mock => mock.Verify(expression, times ?? Times.AtLeastOnce())); 110 | return sut; 111 | } 112 | 113 | /// 114 | /// Verify mock service short method. 115 | /// 116 | /// 117 | /// Expression for verify 118 | /// Mocked service type 119 | /// 120 | public static SystemUnderTest VerifyCallOnce(this SystemUnderTest sut, 121 | Expression> expression) 122 | where TService : class 123 | { 124 | sut.UseServiceMock(mock => mock.Verify(expression, Times.Once)); 125 | return sut; 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Moq/Wd3w.AspNetCore.EasyTesting.Moq.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageIdBase).Moq 5 | bin\Debug\Wd3w.AspNetCore.EasyTesting.Moq.xml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.NSubstitute/SystemUnderTestNSubstituteExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using NSubstitute; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.NSubstitute 7 | { 8 | /// 9 | /// NSubstitute extension helper for SystemUnderTestBase 10 | /// 11 | public static class SystemUnderTestNSubstituteExtensions 12 | { 13 | /// 14 | /// Replace original registry with NSubstitute object. If you call this replace method multiple times, the substitute is created only once. 15 | /// 16 | /// 17 | /// substitute 18 | /// Service type 19 | /// 20 | public static SystemUnderTest ReplaceWithSubstitute(this SystemUnderTest sut, out T substitute) where T : class 21 | { 22 | sut.CheckClientIsNotCreated(nameof(ReplaceWithSubstitute)); 23 | substitute = sut.GetOrAddInternalService(() => Substitute.For()); 24 | sut.ReplaceService(substitute); 25 | return sut; 26 | } 27 | 28 | /// 29 | /// Replace original registry with NSubstitute object. If you call this replace method multiple times, the substitute is created only once. 30 | /// 31 | /// 32 | /// Setup substitute 33 | /// Service type 34 | /// 35 | public static SystemUnderTest ReplaceWithSubstitute(this SystemUnderTest sut, [NotNull] Action setup) where T : class 36 | { 37 | sut.ReplaceWithSubstitute(out var substitute); 38 | setup(substitute); 39 | return sut; 40 | } 41 | 42 | /// 43 | /// Get pre-registered NSubstitute object. 44 | /// 45 | /// 46 | /// Service type 47 | /// 48 | public static T GetSubstitute(this SystemUnderTest sut) 49 | { 50 | return sut.InternalServiceProvider.GetService() ?? throw new InvalidOperationException("Replace first using ReplaceWithNSubstitute before call UseSubstitute."); 51 | } 52 | 53 | /// 54 | /// Get pre-registered NSubstitute object. 55 | /// 56 | /// 57 | /// 58 | /// Service type 59 | /// 60 | public static SystemUnderTest GetSubstitute(this SystemUnderTest sut, out T substitute) 61 | { 62 | substitute = sut.GetSubstitute(); 63 | return sut; 64 | } 65 | 66 | /// 67 | /// Use pre-registered NSubstitute object. 68 | /// 69 | /// 70 | /// 71 | /// Service type 72 | /// 73 | public static SystemUnderTest UseSubstitute(this SystemUnderTest sut, Action verify) 74 | { 75 | var service = sut.GetSubstitute(); 76 | verify(service); 77 | return sut; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.NSubstitute/Wd3w.AspNetCore.EasyTesting.NSubstitute.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageIdBase).NSubstitute 5 | bin\Debug\Wd3w.AspNetCore.EasyTesting.NSubstitute.xml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Options; 7 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Models; 8 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 9 | 10 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Controllers 11 | { 12 | [ApiController] 13 | [Route("api/sample")] 14 | public class SampleController : ControllerBase 15 | { 16 | private readonly ILogger _logger; 17 | private readonly ISampleService _service; 18 | private readonly SampleRepository _repository; 19 | private readonly IHostEnvironment _environment; 20 | private readonly IOptionsSnapshot _optionsSnapshot; 21 | 22 | public SampleController(ILogger logger, ISampleService service, SampleRepository repository, IHostEnvironment environment, IOptionsSnapshot optionsSnapshot) 23 | { 24 | _logger = logger; 25 | _service = service; 26 | _repository = repository; 27 | _environment = environment; 28 | _optionsSnapshot = optionsSnapshot; 29 | } 30 | 31 | 32 | [HttpGet("data")] 33 | public ActionResult GetSampleData() 34 | { 35 | _logger.LogInformation("Call Get SampleData API!"); 36 | return new SampleDataResponse 37 | { 38 | Data = _service.GetSampleDate() 39 | }; 40 | } 41 | 42 | [HttpGet("sample-data-from-db")] 43 | public async Task GetRepositorySamplesAsync() 44 | { 45 | return Ok(await _repository.GetSamplesAsync()); 46 | } 47 | 48 | [Authorize] 49 | [HttpGet("secure")] 50 | public ActionResult GetSecureProcess() 51 | { 52 | return NoContent(); 53 | } 54 | 55 | [HttpGet("environment")] 56 | public string GetEnvironment() 57 | { 58 | return _environment.EnvironmentName; 59 | } 60 | 61 | [HttpGet("configuration")] 62 | public SampleOption GetConfiguration() 63 | { 64 | return _optionsSnapshot.Value; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Entities/SampleDb.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Entities 5 | { 6 | public class SampleDb : DbContext 7 | { 8 | public SampleDb(DbContextOptions options) : base(options) 9 | { 10 | } 11 | 12 | public DbSet SampleDataEntities { get; set; } 13 | } 14 | 15 | public class SampleDataEntity 16 | { 17 | [Key] 18 | public long Id { get; set; } 19 | 20 | public string Data { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Models/SampleDataResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Models 2 | { 3 | public class SampleDataResponse 4 | { 5 | public string Data { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) 14 | { 15 | return Host.CreateDefaultBuilder(args) 16 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Wd3w.AspNewCore.EasyTesting.SampleApi": { 4 | "commandName": "Project", 5 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Services/CustomTokenAuthService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using System.Threading.Tasks; 3 | using Wd3w.TokenAuthentication; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Services 6 | { 7 | public class CustomTokenAuthService : ITokenAuthService 8 | { 9 | public async Task IsValidateAsync(string token) 10 | { 11 | return true; 12 | } 13 | 14 | public async Task GetPrincipalAsync(string token) 15 | { 16 | // Do create your own custom claims pricipal object and return them; 17 | return new ClaimsPrincipal(new ClaimsIdentity(new [] 18 | { 19 | new Claim(ClaimTypes.Email, "test@test.com"), 20 | new Claim(ClaimTypes.Name, "test-user") 21 | })); 22 | } 23 | }} -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Services/ISampleService.cs: -------------------------------------------------------------------------------- 1 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Services 2 | { 3 | public interface ISampleService 4 | { 5 | public string GetSampleDate(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Services/SampleRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Microsoft.EntityFrameworkCore; 4 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Entities; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Services 7 | { 8 | public class SampleRepository 9 | { 10 | private readonly SampleDb _db; 11 | 12 | public SampleRepository(SampleDb db) 13 | { 14 | _db = db; 15 | } 16 | 17 | public async Task> GetSamplesAsync() 18 | { 19 | return await _db.SampleDataEntities.ToListAsync(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Services/SampleService.cs: -------------------------------------------------------------------------------- 1 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi.Services 2 | { 3 | public class SampleService : ISampleService 4 | { 5 | public string GetSampleDate() 6 | { 7 | return "Original Sample Data"; 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Options; 11 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Entities; 12 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 13 | using Wd3w.TokenAuthentication; 14 | 15 | namespace Wd3w.AspNetCore.EasyTesting.SampleApi 16 | { 17 | public class Startup 18 | { 19 | public IConfiguration Configuration { get; } 20 | 21 | public Startup(IConfiguration configuration) 22 | { 23 | Configuration = configuration; 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddControllers(); 31 | services.AddOptions() 32 | .Bind(Configuration) 33 | .ValidateDataAnnotations() 34 | .ValidateOnStartupTime(); 35 | services.AddScoped(); 36 | services.AddScoped(); 37 | services.AddDbContext(); 38 | 39 | services.AddDistributedRedisCache(options => 40 | { 41 | options.InstanceName = "test-redis"; 42 | options.Configuration = "host=127.0.0.1"; 43 | }); 44 | services.AddSingleton(new DbContextOptionsBuilder() 45 | .UseNpgsql("Host=my_host;Database=my_db;Username=my_user;Password=my_pw") 46 | .Options); 47 | 48 | services.AddAuthentication("Bearer") 49 | .AddTokenAuthenticationScheme("Bearer", new TokenAuthenticationConfiguration 50 | { 51 | Realm = "https://www.test.com/sign-in", 52 | TokenLength = 20 53 | }); 54 | } 55 | 56 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 57 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 58 | { 59 | if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); 60 | 61 | app.UseRouting(); 62 | app.UseAuthentication(); 63 | app.UseAuthorization(); 64 | app.UseEndpoints(endpoints => 65 | { 66 | endpoints.MapControllers(); 67 | endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); 68 | }); 69 | } 70 | } 71 | 72 | public static class OptionsBuilderExtensions 73 | { 74 | public static OptionsBuilder ValidateOnStartupTime(this OptionsBuilder builder) 75 | where TOptions : class 76 | { 77 | builder.Services.AddTransient>(); 78 | return builder; 79 | } 80 | 81 | public class OptionsValidateFilter : IStartupFilter where TOptions : class 82 | { 83 | private readonly IOptions _options; 84 | 85 | public OptionsValidateFilter(IOptions options) 86 | { 87 | _options = options; 88 | } 89 | 90 | public Action Configure(Action next) 91 | { 92 | _ = _options.Value; // Trigger for validating options. 93 | return next; 94 | } 95 | } 96 | } 97 | 98 | public class SampleOption 99 | { 100 | [Required] 101 | public string NeedValue { get; set; } 102 | } 103 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/Wd3w.AspNetCore.EasyTesting.SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "NeedValue": "Sample", 10 | "AllowedHosts": "*" 11 | } 12 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Authentication/FakeAuthenticationTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Security.Claims; 3 | using System.Threading.Tasks; 4 | using Hestify; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Wd3w.AspNetCore.EasyTesting.Authentication; 7 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 8 | using Xunit; 9 | 10 | namespace Wd3w.AspNetCore.EasyTesting.Test.Authentication 11 | { 12 | public class FakeAuthenticationTest : EasyTestingTestBase 13 | { 14 | [Fact] 15 | public async Task Test() 16 | { 17 | var httpClient = SUT 18 | .NoUserAuthentication("Bearer") 19 | .CreateClient(); 20 | 21 | var message = await httpClient.Resource("api/sample/secure").GetAsync(); 22 | 23 | message.ShouldBe(HttpStatusCode.Unauthorized); 24 | 25 | SUT.FakeAuthentication("Bearer", AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal( 26 | new ClaimsIdentity(new[] 27 | { 28 | new Claim(ClaimTypes.Email, "test@test.com"), 29 | }, "Bearer")), "Bearer"))); 30 | var message2 = await httpClient.Resource("api/sample/secure").GetAsync(); 31 | message2.ShouldBe(HttpStatusCode.NoContent); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Authentication/NoUserAuthenticationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using FluentAssertions; 6 | using Hestify; 7 | using Wd3w.AspNetCore.EasyTesting.Authentication; 8 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 9 | using Wd3w.AspNetCore.EasyTesting.Test.Helper; 10 | using Xunit; 11 | 12 | namespace Wd3w.AspNetCore.EasyTesting.Test.Authentication 13 | { 14 | public class NoUserAuthenticationTest : EasyTestingTestBase 15 | { 16 | [Fact] 17 | public async Task PassTest() 18 | { 19 | var httpClient = SUT.CreateClient(); 20 | 21 | var message = await CallAuthorizedApiWithAccessTokenAsync(httpClient); 22 | 23 | message.ShouldBe(HttpStatusCode.NoContent); 24 | } 25 | 26 | [Fact] 27 | public async Task NoUserAuthenticationDefaultSchemeTest() 28 | { 29 | var httpClient = SUT 30 | .NoUserAuthentication() 31 | .CreateClient(); 32 | 33 | var message = await CallAuthorizedApiWithAccessTokenAsync(httpClient); 34 | 35 | message.ShouldBe(HttpStatusCode.Unauthorized); 36 | } 37 | 38 | private static Task CallAuthorizedApiWithAccessTokenAsync(HttpClient httpClient) 39 | { 40 | return httpClient 41 | .Resource("api/sample/secure") 42 | .WithFakeBearerToken() 43 | .GetAsync(); 44 | } 45 | 46 | [Fact] 47 | public async Task NoUserAuthenticationSpecificSchemeTest() 48 | { 49 | var httpClient = SUT 50 | .NoUserAuthentication("Bearer") 51 | .CreateClient(); 52 | 53 | var message = await CallAuthorizedApiWithAccessTokenAsync(httpClient); 54 | 55 | message.ShouldBe(HttpStatusCode.Unauthorized); 56 | } 57 | 58 | [Fact] 59 | public void NoUserAuthenticationWithWrongAuthorizationSchemeTest() 60 | { 61 | Func createClient = SUT.NoUserAuthentication("WrongScheme").CreateClient; 62 | 63 | createClient.Should().Throw(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Common/EasyTestingTestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Wd3w.AspNetCore.EasyTesting.SampleApi; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Test.Common 5 | { 6 | public abstract class EasyTestingTestBase : IDisposable 7 | { 8 | // ReSharper disable once InconsistentNaming 9 | protected readonly SystemUnderTest SUT; 10 | 11 | protected EasyTestingTestBase() 12 | { 13 | SUT = new SystemUnderTest(); 14 | } 15 | 16 | public void Dispose() 17 | { 18 | SUT?.Dispose(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Common/FakeSampleService.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 2 | 3 | namespace Wd3w.AspNetCore.EasyTesting.Test.Common 4 | { 5 | public class FakeSampleService : ISampleService 6 | { 7 | public string Message { get; set; } = "Fake!"; 8 | 9 | public string GetSampleDate() 10 | { 11 | return Message; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Common/INeverRegisteredServiceType.cs: -------------------------------------------------------------------------------- 1 | namespace Wd3w.AspNetCore.EasyTesting.Test.Common 2 | { 3 | public interface INeverRegisteredServiceType 4 | { 5 | } 6 | 7 | public class NeverRegisteredServiceType : INeverRegisteredServiceType 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Common/TestApiFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Testing; 2 | using Wd3w.AspNetCore.EasyTesting.SampleApi; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Test.Common 5 | { 6 | public class TestApiFactory : WebApplicationFactory 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Helper/HestifyClientHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Hestify; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Test.Helper 5 | { 6 | public static class HestifyClientHelper 7 | { 8 | public static HestifyClient WithBearerToken(this HestifyClient client, string token) 9 | { 10 | return client.WithHeader(HttpRequestHeader.Authorization, $"Bearer ${token}"); 11 | } 12 | 13 | public static HestifyClient WithFakeBearerToken(this HestifyClient client) 14 | { 15 | return client.WithBearerToken("01234567890123456789"); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/HestifyExtension/HestifyExtensionHelperTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Wd3w.AspNetCore.EasyTesting.Hestify; 3 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 4 | using Xunit; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.Test.HestifyExtension 7 | { 8 | public class HestifyExtensionHelperTest : EasyTestingTestBase 9 | { 10 | [Fact] 11 | public async Task Test() 12 | { 13 | // Given 14 | // When 15 | var message = await SUT.Resource("api/sample/data").GetAsync(); 16 | 17 | // Then 18 | message.ShouldBeOk(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/InMemoryDbContext/InMemoryDbContextHelperTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using Microsoft.EntityFrameworkCore; 6 | using Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore; 7 | using Wd3w.AspNetCore.EasyTesting.Hestify; 8 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Entities; 9 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 10 | using Xunit; 11 | 12 | namespace Wd3w.AspNetCore.EasyTesting.Test.InMemoryDbContext 13 | { 14 | public class InMemoryDbContextHelperTest : EasyTestingTestBase 15 | { 16 | [Fact] 17 | public async Task Should_BeFine_When_ReplaceInMemoryDbContext() 18 | { 19 | // Given 20 | SUT.ReplaceInMemoryDbContext(); 21 | 22 | // When 23 | Action action = () => SUT.CreateClient(); 24 | 25 | // Then 26 | action.Should().NotThrow(); 27 | } 28 | 29 | [Fact] 30 | public async Task ReplaceInMemoryDbContextTest() 31 | { 32 | // Given 33 | SUT.ReplaceInMemoryDbContext() 34 | .SetupFixture(async db => 35 | { 36 | await db.SampleDataEntities.AddAsync(new SampleDataEntity 37 | { 38 | Data = "Sample Data" 39 | }); 40 | await db.SaveChangesAsync(); 41 | }); 42 | 43 | // When 44 | SUT.CreateClient(); 45 | 46 | // Then 47 | await SUT.UsingServiceAsync(async db => (await db.SampleDataEntities.CountAsync()).Should().Be(1)); 48 | } 49 | 50 | [Fact] 51 | public async Task SampleApiE2ETest() 52 | { 53 | // Given 54 | SUT.ReplaceInMemoryDbContext() 55 | .SetupFixture(async db => 56 | { 57 | await db.SampleDataEntities.AddRangeAsync(new[] 58 | { 59 | new SampleDataEntity {Data = "Hi"}, 60 | new SampleDataEntity {Data = "Hi"}, 61 | new SampleDataEntity {Data = "Hi"}, 62 | new SampleDataEntity {Data = "Hi"} 63 | }); 64 | 65 | await db.SaveChangesAsync(); 66 | }); 67 | 68 | // When 69 | var message = await SUT.Resource("api/sample/sample-data-from-db").GetAsync(); 70 | 71 | // Then 72 | await message.ShouldBeOk>(entities => 73 | { 74 | entities.Should().HaveCount(4); 75 | }); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/InMemoryDbContext/SqliteInMemoryDbContextHelperTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Microsoft.EntityFrameworkCore; 5 | using Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore; 6 | using Wd3w.AspNetCore.EasyTesting.Hestify; 7 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Entities; 8 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 9 | using Xunit; 10 | 11 | namespace Wd3w.AspNetCore.EasyTesting.Test.InMemoryDbContext 12 | { 13 | public class SqliteInMemoryDbContextHelperTest : EasyTestingTestBase 14 | { 15 | [Fact] 16 | public async Task ReplaceSqliteInMemoryDbContextTest() 17 | { 18 | // Given 19 | SUT.ReplaceSqliteInMemoryDbContext() 20 | .SetupFixture(async db => 21 | { 22 | await db.SampleDataEntities.AddAsync(new SampleDataEntity 23 | { 24 | Data = "Sample Data" 25 | }); 26 | await db.SaveChangesAsync(); 27 | }); 28 | 29 | // When 30 | SUT.CreateClient(); 31 | 32 | // Then 33 | await SUT.UsingServiceAsync(async db => (await db.SampleDataEntities.CountAsync()).Should().Be(1)); 34 | } 35 | 36 | [Fact] 37 | public async Task SampleApiE2ETest() 38 | { 39 | // Given 40 | SUT.ReplaceSqliteInMemoryDbContext() 41 | .SetupFixture(async db => 42 | { 43 | await db.SampleDataEntities.AddRangeAsync(new[] 44 | { 45 | new SampleDataEntity {Data = "Hi"}, 46 | new SampleDataEntity {Data = "Hi"}, 47 | new SampleDataEntity {Data = "Hi"}, 48 | new SampleDataEntity {Data = "Hi"} 49 | }); 50 | 51 | await db.SaveChangesAsync(); 52 | }); 53 | 54 | // When 55 | var message = await SUT.Resource("api/sample/sample-data-from-db").GetAsync(); 56 | 57 | // Then 58 | await message.ShouldBeOk>(entities => 59 | { 60 | entities.Should().HaveCount(4); 61 | }); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/MockSupports/FakeItEasyExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using FakeItEasy; 2 | using FluentAssertions; 3 | using Wd3w.AspNetCore.EasyTesting.FakeItEasy; 4 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 5 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 6 | using Xunit; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.MockSupports 9 | { 10 | public class FakeItEasyExtensionsTest : EasyTestingTestBase 11 | { 12 | [Fact] 13 | public void DummyService_Should_ReturnEmpty_When_ServiceIsReplacedWithDummy() 14 | { 15 | // Given 16 | // When 17 | SUT.ReplaceDummyService() 18 | .CreateClient(); 19 | 20 | // THen 21 | SUT.UsingService(service => service.GetSampleDate().Should().BeEmpty()); 22 | } 23 | 24 | [Fact] 25 | public void FakeService_Should_CanBeMocked_When_ServiceIsReplacedFakeObject() 26 | { 27 | // Given 28 | // When 29 | SUT.FakeService(service => A 30 | .CallTo(() => service.GetSampleDate()) 31 | .Returns("MockedData")) 32 | .CreateClient(); 33 | 34 | // Then 35 | SUT.UsingService(service => service.GetSampleDate().Should().Be("MockedData")); 36 | } 37 | 38 | [Fact] 39 | public void GetFakeService_Should_ReturnSameObjectWithRegisteredObject_When_ServiceIsFaked() 40 | { 41 | // Given 42 | var service = SUT.FakeService(); 43 | 44 | // When 45 | SUT.CreateClient(); 46 | 47 | // Then 48 | var fakeService = SUT.GetFakeService(); 49 | fakeService.Should().BeSameAs(service); 50 | } 51 | 52 | [Fact] 53 | public void UseFakeService_Should_ProvideSameObjectWithRegisteredObject_When_ServiceIsFaked() 54 | { 55 | // Given 56 | var service = SUT.FakeService(); 57 | 58 | // When 59 | SUT.CreateClient(); 60 | 61 | // Then 62 | SUT.UseFakeService(fake => fake.Should().BeSameAs(service)); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/MockSupports/MoqExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Hestify; 5 | using Moq; 6 | using Wd3w.AspNetCore.EasyTesting.Moq; 7 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Models; 8 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 9 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 10 | using Xunit; 11 | 12 | namespace Wd3w.AspNetCore.EasyTesting.Test.MockSupports 13 | { 14 | public class MockServiceTest : EasyTestingTestBase 15 | { 16 | [Fact] 17 | public async Task Should_ReplaceWithMockObjectOfServiceType() 18 | { 19 | // Given 20 | var httpClient = SUT.MockService(out var mock) 21 | .CreateClient(); 22 | 23 | mock.Setup(service => service.GetSampleDate()) 24 | .Returns("Mock Return!"); 25 | 26 | // When 27 | var message = await httpClient.Resource("api/sample/data").GetAsync(); 28 | 29 | // Then 30 | var sample = await message.ShouldBeOk(); 31 | sample.Data.Should().Be("Mock Return!"); 32 | mock.Verify(service => service.GetSampleDate(), Times.Once); 33 | } 34 | 35 | [Fact] 36 | public async Task Should_ReplaceWithMockObjectOfServiceType_When_ProvidedByAction() 37 | { 38 | // Given 39 | var httpClient = SUT.MockService(_ => _ 40 | .Setup(service => service.GetSampleDate()) 41 | .Returns("Action mocked!")) 42 | .CreateClient(); 43 | 44 | // When 45 | var message = await httpClient.Resource("api/sample/data").GetAsync(); 46 | 47 | // Then 48 | var sample = await message.ShouldBeOk(); 49 | sample.Data.Should().Be("Action mocked!"); 50 | } 51 | 52 | [Fact] 53 | public void Should_ThrowException_When_TryToCallMockServiceAfterCreateClient() 54 | { 55 | // Given 56 | SUT.CreateClient(); 57 | 58 | // When 59 | Action callMockService = () => SUT.MockService(out _); 60 | 61 | // Then 62 | callMockService.Should().Throw(); 63 | } 64 | 65 | [Fact] 66 | public void Should_ReturnMockObject_When_RegisteredMockObject() 67 | { 68 | // Given 69 | SUT.MockService(out _) 70 | .CreateClient(); 71 | 72 | // When 73 | var mock = SUT.GetServiceMock(); 74 | 75 | // Then 76 | mock.Should().NotBeNull(); 77 | } 78 | 79 | [Fact] 80 | public void Should_ThrowInvalidOperationException_When_TheServiceIsNotReplacedWithMock() 81 | { 82 | // Given 83 | SUT.CreateClient(); 84 | 85 | // When 86 | Action action = () => SUT.GetServiceMock(); 87 | 88 | // Then 89 | action.Should().Throw(); 90 | } 91 | 92 | [Fact] 93 | public async Task Should_CanVerifyCalledWithVerifyCallWithTimesOnce_When_TheApiIsCalled() 94 | { 95 | // Given 96 | var httpClient = SUT.MockService(_ => _ 97 | .Setup(service => service.GetSampleDate()) 98 | .Returns("Mock Return!")) 99 | .CreateClient(); 100 | 101 | // When 102 | await httpClient.Resource("api/sample/data").GetAsync(); 103 | 104 | // Then 105 | SUT.VerifyCall(service => service.GetSampleDate(), Times.Once()); 106 | } 107 | 108 | [Fact] 109 | public async Task Should_VerifyAtLeastOnce_When_TheTimesParameterIsNotProvided() 110 | { 111 | // Given 112 | var httpClient = SUT.MockService(_ => _ 113 | .Setup(service => service.GetSampleDate()) 114 | .Returns("Mock Return!")) 115 | .CreateClient(); 116 | 117 | // When 118 | await httpClient.Resource("api/sample/data").GetAsync(); 119 | 120 | // Then 121 | SUT.VerifyCall(service => service.GetSampleDate()); 122 | } 123 | 124 | [Fact] 125 | public async Task Should_VerifyCallOnce_When_TheApiIsCalledOneTime() 126 | { 127 | // Given 128 | var httpClient = SUT.MockService(_ => _ 129 | .Setup(service => service.GetSampleDate()) 130 | .Returns("Mock Return!")) 131 | .CreateClient(); 132 | 133 | // When 134 | await httpClient.Resource("api/sample/data").GetAsync(); 135 | 136 | // Then 137 | SUT.VerifyCallOnce(service => service.GetSampleDate()); 138 | } 139 | 140 | [Fact] 141 | public void GetServiceMock_Should_GettingMockObjectIsSameAsPreviousMockObject_When_TheServiceIsAlreadyMocked() 142 | { 143 | // Given 144 | var mockService = SUT.MockService(); 145 | 146 | // When 147 | var gettingService = SUT.GetServiceMock(); 148 | 149 | // Then 150 | mockService.Should().BeSameAs(gettingService); 151 | } 152 | 153 | [Fact] 154 | public void MockService_Should_ReturnSameMockObject_When_MockServiceIsCalledMultipleTimes() 155 | { 156 | // Given 157 | // When 158 | var m1 = SUT.MockService(); 159 | var m2 = SUT.MockService(); 160 | 161 | // Then 162 | m1.Should().BeSameAs(m2); 163 | } 164 | 165 | [Fact] 166 | public void UseServiceMock_Should_ReturnSameMockObject_When_TheServiceIsReplacedAlready() 167 | { 168 | // Given 169 | // When 170 | var mockService = SUT.MockService(); 171 | 172 | // Then 173 | SUT.UseServiceMock(mock => mock.Should().BeSameAs(mockService)); 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/MockSupports/NSubstituteExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using NSubstitute; 4 | using Wd3w.AspNetCore.EasyTesting.Hestify; 5 | using Wd3w.AspNetCore.EasyTesting.NSubstitute; 6 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Models; 7 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 8 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 9 | using Xunit; 10 | 11 | namespace Wd3w.AspNetCore.EasyTesting.Test.MockSupports 12 | { 13 | public class NSubstituteExtensionsTest : EasyTestingTestBase 14 | { 15 | [Fact] 16 | public void ReplaceWithNSubstitute_Should_RegisterOnInternalService_When_ServiceIsReplacedWithSubstitute() 17 | { 18 | // Given 19 | SUT.ReplaceWithSubstitute(out var mockedService); 20 | 21 | // When 22 | SUT.CreateClient(); 23 | 24 | // Then 25 | SUT.UsingService(service => service.Should().BeSameAs(mockedService)); 26 | } 27 | 28 | [Fact] 29 | public async Task ReplaceWithNSubstitute_Should_ReturnMockedDataFromSubstitute_When_ServiceIsReplacedWithSubstitute() 30 | { 31 | // Given 32 | SUT.ReplaceWithSubstitute(service => service.GetSampleDate().Returns("Mocked Data")); 33 | 34 | // When 35 | var message = await SUT.Resource("api/sample/data").GetAsync(); 36 | 37 | // Then 38 | await message.ShouldBeOk(res => res.Data.Should().Be("Mocked Data")); 39 | } 40 | 41 | [Fact] 42 | public async Task ReplaceWithNSubstitute_Should_CheckCallGetSampleData_When_ServiceIsReplacedWithSubstitute() 43 | { 44 | // Given 45 | SUT.ReplaceWithSubstitute(service => service.GetSampleDate().Returns("Mocked Data")); 46 | 47 | // When 48 | await SUT.Resource("api/sample/data").GetAsync(); 49 | 50 | // Then 51 | SUT.GetSubstitute().Received().GetSampleDate(); 52 | } 53 | 54 | [Fact] 55 | public async Task 56 | ReplaceWithNSubstitute_Should_ReturnSameMockedObject_When_ReplaceWithNSubstituteIsCalledMultipleTimes() 57 | { 58 | // Given 59 | // When 60 | SUT.ReplaceWithSubstitute(out var s1) 61 | .ReplaceWithSubstitute(out var s2); 62 | 63 | // Then 64 | s1.Should().BeSameAs(s2); 65 | } 66 | 67 | [Fact] 68 | public void GetSubstitute_Should_ReturnSameAsRegisteredMockObject_When_TheServiceIsAlreadyMocked() 69 | { 70 | // Given 71 | SUT.ReplaceWithSubstitute(out var service); 72 | 73 | // When 74 | var gettingService = SUT.GetSubstitute(); 75 | 76 | // Then 77 | service.Should().BeSameAs(gettingService); 78 | } 79 | 80 | [Fact] 81 | public void 82 | UseSubstitute_Should_SameObjectBetweenPreRegisteredObjectAndProvidedObject_When_TheServiceIsAlreadyMocked() 83 | { 84 | // Given 85 | // When 86 | SUT.ReplaceWithSubstitute(out var service); 87 | 88 | // Then 89 | SUT.UseSubstitute(mock => service.Should().BeSameAs(mock)); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SampleTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore; 6 | using Wd3w.AspNetCore.EasyTesting.Hestify; 7 | using Wd3w.AspNetCore.EasyTesting.Moq; 8 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Entities; 9 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 10 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 11 | using Xunit; 12 | 13 | namespace Wd3w.AspNetCore.EasyTesting.Test 14 | { 15 | public class SampleTest : EasyTestingTestBase 16 | { 17 | [Fact] 18 | public async Task EasyTesting_Make_IntegrationTest_Easy() 19 | { 20 | // Given 21 | SUT.ReplaceInMemoryDbContext() 22 | .SetupFixture(async db => 23 | { 24 | db.SampleDataEntities.Add(new SampleDataEntity()); 25 | await db.SaveChangesAsync(); 26 | }) 27 | .MockService(mock => mock 28 | .Setup(s => s.GetSampleDate()) 29 | .Returns("MockedData")); 30 | 31 | // When 32 | await SUT.Resource("api/get/sample").GetAsync(); 33 | 34 | // Then 35 | SUT.UsingService(service => service.GetSampleDate().Should().Be("MockedData")); 36 | SUT.VerifyCallOnce(service => service.GetSampleDate()); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ConfigureAppConfigurationTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class ConfigureAppConfigurationTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_CallBackIsCalled_When_CreateClient() 11 | { 12 | var called = false; 13 | SUT.ConfigureAppConfiguration(builder => called = true) 14 | .CreateClient(); 15 | 16 | called.Should().BeTrue(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ConfigureServicesTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 4 | using Xunit; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 7 | { 8 | public class ConfigureServicesTest : EasyTestingTestBase 9 | { 10 | public class TestService{} 11 | 12 | [Fact] 13 | public void Should_BeRegistered_When_AdditionalServiceIsAddedByConfigureService() 14 | { 15 | // Given 16 | SUT.ConfigureServices(services => services.AddSingleton(new TestService())); 17 | 18 | // When 19 | SUT.CreateClient(); 20 | 21 | // Then 22 | SUT.UsingService(service => service.Should().NotBeNull()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/CreateClientTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Hestify; 4 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Models; 5 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 6 | using Xunit; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class CreateClientTest : EasyTestingTestBase 11 | { 12 | [Fact] 13 | public async Task Should_ReturnHttpClientAndWork() 14 | { 15 | // Given 16 | // WHen 17 | var httpClient = SUT.CreateClient(); 18 | var response = await httpClient.Resource("api/sample/data").GetAsync(); 19 | 20 | // Then 21 | var sample = await response.ShouldBeOk(); 22 | sample.Data.Should().Be("Original Sample Data"); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/DisableOptionDataAnnotationValidationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Microsoft.Extensions.Options; 5 | using Wd3w.AspNetCore.EasyTesting.SampleApi; 6 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 7 | using Xunit; 8 | 9 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 10 | { 11 | public class DisableOptionDataAnnotationValidationTest : EasyTestingTestBase 12 | { 13 | [Fact] 14 | public void Should_BeWorkingNormally_When_AllConfigurationIsExactlyFine() 15 | { 16 | // Given 17 | // When 18 | SUT.CreateClient(); 19 | 20 | // Then 21 | SUT.UsingService>(snapshot => snapshot.Value.NeedValue.Should().Be("Sample")); 22 | } 23 | 24 | [Fact] 25 | public void Should_ThrowOptionsValidationException_When_ConfigurationIsInvalid() 26 | { 27 | // Given 28 | SUT.OverrideAppConfiguration(new {NeedValue = ""}); 29 | 30 | // When 31 | Action startup = () => SUT.CreateClient(); 32 | 33 | // Then 34 | startup.Should().Throw(); 35 | } 36 | 37 | [Fact] 38 | public void Should_NotThingToBeThrown_When_DisableOptionDataAnnotationValidationAlthoughConfigurationIsInvalid() 39 | { 40 | // Given 41 | SUT.OverrideAppConfiguration(new {NeedValue = ""}) 42 | .DisableOptionDataAnnotationValidation(); 43 | 44 | // When 45 | Action startup = () => SUT.CreateClient(); 46 | 47 | // Then 48 | startup.Should().NotThrow(); 49 | } 50 | 51 | [Fact] 52 | public void Should_NothingToBeThrown_When_DisableAllOptionValidationsAlthoughConfigurationIsInvalid() 53 | { 54 | // Given 55 | SUT.OverrideAppConfiguration(new {NeedValue = ""}) 56 | .DisableOptionValidations(); 57 | 58 | // When 59 | Action startup = () => SUT.CreateClient(); 60 | 61 | // Then 62 | startup.Should().NotThrow(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/DisableStartupFiltersTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class DisableStartupFiltersTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_RemoveIStartFilterRegistration_When_CallDisableStartupFilters() 11 | { 12 | // Given 13 | SUT.DisableStartupFilters(); 14 | 15 | // When 16 | SUT.CreateClient(); 17 | 18 | // Then 19 | SUT.VerifyNoRegistration(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/GetOrAddInternalServiceTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 4 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 5 | using Xunit; 6 | 7 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 8 | { 9 | public class GetOrAddInternalServiceTest : EasyTestingTestBase 10 | { 11 | [Fact] 12 | public void Should_ReturnObject_When_TheServiceIsNotRegistered() 13 | { 14 | // Given 15 | // When 16 | var service = SUT.GetOrAddInternalService(() => new FakeSampleService()); 17 | 18 | // Then 19 | service.Should().NotBeNull(); 20 | } 21 | 22 | [Fact] 23 | public void Should_ReturnSameObject_When_TheServiceIsAlreadyRegistered() 24 | { 25 | // Given 26 | var service = new FakeSampleService(); 27 | SUT.InternalServiceCollection.AddSingleton(service); 28 | 29 | // When 30 | var gettingService = SUT.GetOrAddInternalService(() => new FakeSampleService()); 31 | 32 | // Then 33 | service.Should().BeSameAs(gettingService); 34 | } 35 | 36 | [Fact] 37 | public void Should_ReturnSameObject_When_TheAddMethodIsCalledMultipleTimes() 38 | { 39 | // Given 40 | var preRegisteredServiceObject = SUT.GetOrAddInternalService(() => new FakeSampleService()); 41 | 42 | // When 43 | var secondRegisteredService = SUT.GetOrAddInternalService(() => new FakeSampleService()); 44 | 45 | // Then 46 | preRegisteredServiceObject.Should().BeSameAs(secondRegisteredService); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/RemoveAllByTest.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class RemoveAllByTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_RemoveAllRegistration_When_ConditionIsDetermineSampleService() 11 | { 12 | // Given 13 | SUT.RemoveAllBy(descriptor => descriptor.ServiceType == typeof(ISampleService)); 14 | 15 | // When 16 | SUT.CreateClient(); 17 | 18 | // Then 19 | SUT.VerifyNoRegistration(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/RemoveAllTest.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class RemoveAllTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_DontBeRegisteredSampleService_When_RemoveISampleServiceTypeAsGeneric() 11 | { 12 | // Given 13 | SUT.RemoveAll(); 14 | 15 | // When 16 | SUT.CreateClient(); 17 | 18 | // Then 19 | SUT.VerifyNoRegistration(); 20 | } 21 | 22 | [Fact] 23 | public void Should_DontBeRegisteredSampleService_When_RemoveISampleServiceTypeAsParameter() 24 | { 25 | // Given 26 | SUT.RemoveAll(typeof(ISampleService)); 27 | 28 | // When 29 | SUT.CreateClient(); 30 | 31 | // Then 32 | SUT.VerifyNoRegistration(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/RemoveSingleByTest.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class RemoveSingleByTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_RemoveAllRegistration_When_ConditionIsDetermineSampleService() 11 | { 12 | // Given 13 | SUT.RemoveSingleBy(descriptor => descriptor.ServiceType == typeof(ISampleService)); 14 | 15 | // When 16 | SUT.CreateClient(); 17 | 18 | // Then 19 | SUT.VerifyNoRegistration(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/RemoveTest.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class RemoveTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_DontBeRegisteredSampleService_When_RemoveISampleServiceTypeAsGeneric() 11 | { 12 | // Given 13 | SUT.Remove(); 14 | 15 | // When 16 | SUT.CreateClient(); 17 | 18 | // Then 19 | SUT.VerifyNoRegistration(); 20 | } 21 | 22 | [Fact] 23 | public void Should_DontBeRegisteredSampleService_When_RemoveISampleServiceTypeAsParameter() 24 | { 25 | // Given 26 | SUT.Remove(typeof(ISampleService), typeof(SampleService)); 27 | 28 | // When 29 | SUT.CreateClient(); 30 | 31 | // Then 32 | SUT.VerifyNoRegistration(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ReplaceConfigureOptionsTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.Options; 3 | using Wd3w.AspNetCore.EasyTesting.SampleApi; 4 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 5 | using Xunit; 6 | 7 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 8 | { 9 | public class ReplaceConfigureOptionsTest : EasyTestingTestBase 10 | { 11 | [Fact] 12 | public void Should_ReplaceOptionConfigurer_When_CallReplaceConfigureOptionsWithCustomCallBack() 13 | { 14 | // Given 15 | var anotherValue = "another value"; 16 | SUT.ReplaceConfigureOptions(option => option.NeedValue = anotherValue); 17 | 18 | // When 19 | SUT.CreateClient(); 20 | 21 | // Then 22 | SUT.UsingService>(snapshot => snapshot.Value.NeedValue.Should().Be(anotherValue)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ReplaceDistributedInMemoryCacheTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | using Microsoft.Extensions.Caching.Redis; 3 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 4 | using Xunit; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 7 | { 8 | public class ReplaceDistributedInMemoryCacheTest : EasyTestingTestBase 9 | { 10 | [Fact] 11 | public void Should_RegisterRedisCacheAsDefault() 12 | { 13 | // Given 14 | // When 15 | SUT.CreateClient(); 16 | 17 | // Then 18 | SUT.VerifyRegisteredImplementationTypeOfService(); 19 | } 20 | 21 | [Fact] 22 | public void 23 | Should_ReplaceImplementationTypeOfCacheToMemoryDistributedCache_When_ReplaceToMemoryCacheBeforeCreateClient() 24 | { 25 | // Given 26 | SUT.ReplaceDistributedInMemoryCache(); 27 | 28 | // When 29 | SUT.CreateClient(); 30 | 31 | // Then 32 | SUT.VerifyRegisteredImplementationTypeOfService(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ReplaceLoggerFactoryTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.Logging; 3 | using Moq; 4 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class ReplaceLoggerFactoryTest : EasyTestingTestBase 11 | { 12 | private readonly ITestOutputHelper _helper; 13 | 14 | public ReplaceLoggerFactoryTest(ITestOutputHelper helper) 15 | { 16 | _helper = helper; 17 | } 18 | 19 | [Fact] 20 | public async Task Should_CallWriteLine_When_ReplaceLoggerFactory() 21 | { 22 | // Given 23 | var mock = new Mock(); 24 | SUT.ReplaceLoggerFactory(builder => builder.AddXUnit(mock.Object).AddXUnit(_helper)); 25 | 26 | // When 27 | var client = SUT.CreateClient(); 28 | await client.GetAsync("api/sample/configuration"); 29 | 30 | // Then 31 | mock.Verify(helper => helper.WriteLine(It.IsAny()), Times.AtLeast(1)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/ReplaceServiceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Hestify; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Models; 7 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 8 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 9 | using Xunit; 10 | 11 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 12 | { 13 | public class ReplaceServiceTest : EasyTestingTestBase 14 | { 15 | public class GuidSampleService : ISampleService 16 | { 17 | public string Message { get; set; } = Guid.NewGuid().ToString(); 18 | 19 | public string GetSampleDate() 20 | { 21 | return Message; 22 | } 23 | } 24 | 25 | [Fact] 26 | public async Task Should_RegisterServiceAsSingleton_With_ScopeIsSingleton() 27 | { 28 | var httpClient = SUT 29 | .ReplaceService(ServiceLifetime.Singleton) 30 | .CreateClient(); 31 | 32 | var response1 = await httpClient.Resource("api/sample/data").GetAsync(); 33 | var response2 = await httpClient.Resource("api/sample/data").GetAsync(); 34 | 35 | var sample1 = await response1.ReadJsonBodyAsync(); 36 | var sample2 = await response2.ReadJsonBodyAsync(); 37 | sample1.Data.Should().Be(sample2.Data); 38 | } 39 | 40 | [Fact] 41 | public async Task Should_RegisterServiceLifeTimeIsSameWithOriginal_With_ScopeIsNotProvided() 42 | { 43 | var httpClient = SUT 44 | .ReplaceService() 45 | .CreateClient(); 46 | 47 | var response1 = await httpClient.Resource("api/sample/data").GetAsync(); 48 | var response2 = await httpClient.Resource("api/sample/data").GetAsync(); 49 | 50 | var sample1 = await response1.ReadJsonBodyAsync(); 51 | var sample2 = await response2.ReadJsonBodyAsync(); 52 | sample1.Data.Should().NotBe(sample2.Data); 53 | } 54 | 55 | [Fact] 56 | public async Task Should_ReplaceService() 57 | { 58 | var httpClient = SUT 59 | .ReplaceService() 60 | .CreateClient(); 61 | 62 | var response = await httpClient.Resource("api/sample/data").GetAsync(); 63 | 64 | var sample = await response.ShouldBeOk(); 65 | sample.Data.Should().Be("Fake!"); 66 | } 67 | 68 | [Fact] 69 | public void Should_ThrowException_When_TryToCallReplaceServiceAfterCreateClient() 70 | { 71 | // Given 72 | SUT.CreateClient(); 73 | 74 | // When 75 | Action callReplaceService = () => SUT.ReplaceService(); 76 | 77 | // Then 78 | callReplaceService.Should().Throw(); 79 | } 80 | 81 | [Fact] 82 | public async Task Should_When_ImplementationObject() 83 | { 84 | var serviceObject = new FakeSampleService 85 | { 86 | Message = "Fake More!" 87 | }; 88 | var httpClient = SUT 89 | .ReplaceService(serviceObject) 90 | .CreateClient(); 91 | 92 | var response = await httpClient.Resource("api/sample/data").GetAsync(); 93 | 94 | var sample = await response.ShouldBeOk(); 95 | sample.Data.Should().Be("Fake More!"); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/SetupFixtureTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Moq; 5 | using Wd3w.AspNetCore.EasyTesting.Moq; 6 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 7 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 8 | using Xunit; 9 | 10 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 11 | { 12 | public class SetupFixtureTest : EasyTestingTestBase 13 | { 14 | [Fact] 15 | public void Should_CallOnceServiceMethod_When_CreateClient() 16 | { 17 | // Given 18 | SUT.MockService(out var mock) 19 | .SetupFixture(service => 20 | { 21 | // Something do buildup fixture 22 | service.GetSampleDate(); 23 | return Task.CompletedTask; 24 | }); 25 | 26 | // When 27 | SUT.CreateClient(); 28 | 29 | // Then 30 | mock.Verify(service => service.GetSampleDate(), Times.Once); 31 | } 32 | 33 | [Fact] 34 | public void Should_CallOnceServiceMethod_When_CreateDefaultClient() 35 | { 36 | // Given 37 | SUT.MockService(out var mock) 38 | .SetupFixture(service => 39 | { 40 | // Something do buildup fixture 41 | service.GetSampleDate(); 42 | return Task.CompletedTask; 43 | }); 44 | 45 | // When 46 | SUT.CreateDefaultClient(); 47 | 48 | // Then 49 | mock.Verify(service => service.GetSampleDate(), Times.Once); 50 | } 51 | 52 | [Fact] 53 | public void Should_ThrowException_When_TryToCallSetupFixtureAfterCreateClient() 54 | { 55 | // Given 56 | SUT.CreateClient(); 57 | 58 | // When 59 | Action callSetupFixture = () => SUT.SetupFixture(service => Task.CompletedTask); 60 | 61 | // Then 62 | callSetupFixture.Should().Throw(); 63 | } 64 | 65 | [Fact] 66 | public void Should_ThrowAllExcpetionOnAggregateException_When_MultipleFixtureInvocationsAreThrowingException() 67 | { 68 | // Given 69 | SUT.SetupFixture(_ => Task.FromException(new Exception())) 70 | .SetupFixture(_ => Task.FromException(new Exception())) 71 | .SetupFixture(_ => Task.FromException(new Exception())) 72 | .SetupFixture(_ => Task.FromException(new Exception())); 73 | 74 | // When 75 | Action action = () => SUT.CreateClient(); 76 | 77 | // Then 78 | action.Should().Throw() 79 | .And.InnerExceptions.Should().HaveCount(4); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/SetupWebHostBuilderTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class SetupWebHostBuilderTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_CalledSetupWebHostBuilderAction_When_CreateClientIsCalled() 11 | { 12 | // Given 13 | var called = false; 14 | SUT.SetupWebHostBuilder(builder => called = true); 15 | 16 | // When 17 | SUT.CreateClient(); 18 | 19 | // Then 20 | called.Should().BeTrue(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/UseEnvironmentTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.Extensions.Hosting; 4 | using Wd3w.AspNetCore.EasyTesting.Hestify; 5 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 6 | using Xunit; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class UseEnvironmentTest : EasyTestingTestBase 11 | { 12 | 13 | [Fact] 14 | public async Task UseDevelopmentEnvironmentTest() 15 | { 16 | // Given 17 | SUT.UseDevelopmentEnvironment(); 18 | 19 | // When 20 | var message = await SUT.Resource("api/sample/environment").GetAsync(); 21 | 22 | // THen 23 | var environment = await message.Content.ReadAsStringAsync(); 24 | environment.Should().Be(Environments.Development); 25 | } 26 | 27 | [Fact] 28 | public async Task UseStagingEnvironmentTest() 29 | { 30 | // Given 31 | SUT.UseStagingEnvironment(); 32 | 33 | // When 34 | var message = await SUT.Resource("api/sample/environment").GetAsync(); 35 | 36 | // THen 37 | var environment = await message.Content.ReadAsStringAsync(); 38 | environment.Should().Be(Environments.Staging); 39 | } 40 | 41 | [Fact] 42 | public async Task UseProductionEnvironmentTest() 43 | { 44 | // Given 45 | SUT.UseProductionEnvironment(); 46 | 47 | // When 48 | var message = await SUT.Resource("api/sample/environment").GetAsync(); 49 | 50 | // Then 51 | var environment = await message.Content.ReadAsStringAsync(); 52 | environment.Should().Be(Environments.Production); 53 | } 54 | 55 | [Fact] 56 | public async Task UseSpecificEnvironmentTest() 57 | { 58 | // Given 59 | SUT.UseEnvironment("Testing"); 60 | 61 | // When 62 | var message = await SUT.Resource("api/sample/environment").GetAsync(); 63 | 64 | // Then 65 | var environment = await message.Content.ReadAsStringAsync(); 66 | environment.Should().Be("Testing"); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/UseSettingTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 3 | using Xunit; 4 | 5 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 6 | { 7 | public class UseSettingTest : EasyTestingTestBase 8 | { 9 | [Fact] 10 | public void Should_SetSettingValue_When_KeyAndValueIsProvided() 11 | { 12 | SUT.UseSetting("config-key", "value"); 13 | 14 | string configSettingValue = default; 15 | SUT 16 | .SetupWebHostBuilder(builder => configSettingValue = builder.GetSetting("config-key")) 17 | .CreateClient(); 18 | 19 | configSettingValue.Should().Be("value"); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/UsingServiceAsyncTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Moq; 5 | using Wd3w.AspNetCore.EasyTesting.Moq; 6 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 7 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 8 | using Xunit; 9 | 10 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 11 | { 12 | public class UsingServiceAsyncTest : EasyTestingTestBase 13 | { 14 | [Fact] 15 | public async Task Should_CallOnceServiceMethod_When_ServiceIsUsedInUsingServiceAsync() 16 | { 17 | // Given 18 | SUT.MockService(out var mock) 19 | .CreateClient(); 20 | 21 | // When 22 | await SUT.UsingServiceAsync(service => 23 | { 24 | service.GetSampleDate(); 25 | return Task.CompletedTask; 26 | }); 27 | 28 | // Then 29 | mock.Verify(service => service.GetSampleDate(), Times.Once); 30 | } 31 | 32 | [Fact] 33 | public void Should_ThrowEcxeption_When_CreateClientIsNotCalledYet() 34 | { 35 | // Given 36 | // When 37 | Action callUsingService = () => SUT.UsingServiceAsync(service => Task.CompletedTask).Wait(); 38 | 39 | // Then 40 | callUsingService.Should().Throw(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/UsingServiceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Moq; 4 | using Wd3w.AspNetCore.EasyTesting.Moq; 5 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 6 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 7 | using Xunit; 8 | 9 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 10 | { 11 | public class UsingServiceTest : EasyTestingTestBase 12 | { 13 | [Fact] 14 | public void Should_CallOnceServiceMethod_When_ServiceIsUsedInUsingServiceAsync() 15 | { 16 | // Given 17 | SUT.MockService(out var mock) 18 | .CreateClient(); 19 | 20 | // When 21 | SUT.UsingService(service => service.GetSampleDate()); 22 | 23 | // Then 24 | mock.Verify(service => service.GetSampleDate(), Times.Once); 25 | } 26 | 27 | [Fact] 28 | public void Should_ThrowException_When_CreateClientIsNotCalledYet() 29 | { 30 | // Given 31 | // When 32 | Action callUsingService = () => SUT.UsingService(service => { }); 33 | 34 | // Then 35 | callUsingService.Should().Throw(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/VerifyNoRegistrationByConditionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 4 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 5 | using Xunit; 6 | using Xunit.Sdk; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class VerifyNoRegistrationByConditionTest : EasyTestingTestBase 11 | { 12 | [Fact] 13 | public void Should_VerifyToSuccess_When_ServiceTypeNeverRegistered() 14 | { 15 | // Given 16 | // When 17 | SUT.CreateClient(); 18 | 19 | // Then 20 | SUT.VerifyNoRegistrationByCondition(descriptor => descriptor.ServiceType == typeof(INeverRegisteredServiceType)); 21 | } 22 | 23 | [Fact] 24 | public void Should_VerifyToFail_When_ServiceTypeNeverRegistered() 25 | { 26 | // Given 27 | // When 28 | SUT.CreateClient(); 29 | Action verify = () => SUT.VerifyNoRegistrationByCondition(descriptor => descriptor.ServiceType == typeof(ISampleService)); 30 | 31 | // Then 32 | verify.Should().Throw(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/VerifyNoRegistrationTest.cs: -------------------------------------------------------------------------------- 1 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 2 | using Xunit; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 5 | { 6 | public class VerifyNoRegistrationTest : EasyTestingTestBase 7 | { 8 | [Fact] 9 | public void Should_Verify_With_OneServiceTypeGenericParameter() 10 | { 11 | // Given 12 | // When 13 | SUT.CreateClient(); 14 | 15 | // Then 16 | SUT.VerifyNoRegistration(); 17 | } 18 | 19 | [Fact] 20 | public void Should_Verify_With_OneServiceTypeInstanceParameter() 21 | { 22 | // Given 23 | // When 24 | SUT.CreateClient(); 25 | 26 | // Then 27 | SUT.VerifyNoRegistration(typeof(INeverRegisteredServiceType)); 28 | } 29 | 30 | [Fact] 31 | public void Should_Verify_With_TwoTypeParameters() 32 | { 33 | // Given 34 | // When 35 | SUT.CreateClient(); 36 | 37 | // Then 38 | SUT.VerifyNoRegistration(); 39 | } 40 | 41 | [Fact] 42 | public void Should_Verify_With_TwoParameter() 43 | { 44 | // Given 45 | // When 46 | SUT.CreateClient(); 47 | 48 | // Then 49 | SUT.VerifyNoRegistration(typeof(INeverRegisteredServiceType), typeof(NeverRegisteredServiceType)); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/VerifyRegisteredLifeTimeOfServiceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 5 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 6 | using Xunit; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class VerifyRegisteredLifeTimeOfServiceTest : EasyTestingTestBase 11 | { 12 | [Fact] 13 | public void Should_ReturnTrue_When_SampleServiceIsRegisteredAsScoped() 14 | { 15 | // Given 16 | // When 17 | SUT.CreateClient(); 18 | 19 | // Then 20 | SUT.VerifyRegisteredLifeTimeOfService(ServiceLifetime.Scoped); 21 | } 22 | 23 | [Fact] 24 | public void Should_ReturnTrue_When_SampleServiceIsRegistredSingleTonAndReplaceServiceWithSingleton() 25 | { 26 | // Given 27 | SUT.ReplaceService(new SampleService()); 28 | 29 | // When 30 | SUT.CreateClient(); 31 | 32 | // Then 33 | SUT.VerifyRegisteredLifeTimeOfService(ServiceLifetime.Singleton); 34 | } 35 | 36 | [Fact] 37 | public void Should_ThrowException_When_CreateClientIsNotCalled() 38 | { 39 | // Given 40 | // When 41 | Action callVerify = () => SUT.VerifyRegisteredLifeTimeOfService(ServiceLifetime.Scoped); 42 | 43 | // Then 44 | callVerify.Should().Throw(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/SystemUnderTest/VerifyRegistrationByConditionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Wd3w.AspNetCore.EasyTesting.SampleApi.Services; 4 | using Wd3w.AspNetCore.EasyTesting.Test.Common; 5 | using Xunit; 6 | using Xunit.Sdk; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting.Test.SystemUnderTest 9 | { 10 | public class VerifyRegistrationByConditionTest : EasyTestingTestBase 11 | { 12 | [Fact] 13 | public void SuccessTest() 14 | { 15 | // Given 16 | var fakeSampleService = new FakeSampleService(); 17 | SUT.ReplaceService(fakeSampleService); 18 | 19 | // When 20 | SUT.CreateClient(); 21 | 22 | // Then 23 | SUT.VerifyRegistrationByCondition(descriptor => descriptor.ServiceType == typeof(ISampleService) && 24 | descriptor.ImplementationInstance == fakeSampleService); 25 | } 26 | 27 | [Fact] 28 | public void FailTest() 29 | { 30 | // Given 31 | SUT.ReplaceService(new FakeSampleService()); 32 | 33 | // When 34 | SUT.CreateClient(); 35 | 36 | // Then 37 | Action action = () => SUT.VerifyRegistrationByCondition(descriptor => descriptor.ServiceType == typeof(ISampleService) && descriptor.ImplementationInstance == null); 38 | action.Should().Throw(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.Test/Wd3w.AspNetCore.EasyTesting.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Items", "Items", "{08FD3B8A-A6D3-43AA-B695-2DF194A360B1}" 4 | ProjectSection(SolutionItems) = preProject 5 | ..\README.md = ..\README.md 6 | .gitignore = .gitignore 7 | Directory.Build.props = Directory.Build.props 8 | ..\build\version.props = ..\build\version.props 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting", "Wd3w.AspNetCore.EasyTesting\Wd3w.AspNetCore.EasyTesting.csproj", "{BF4866CF-ED28-40DA-B79D-FEFF4138A68C}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.SampleApi", "Wd3w.AspNetCore.EasyTesting.SampleApi\Wd3w.AspNetCore.EasyTesting.SampleApi.csproj", "{09304BAD-484E-4EE9-8A30-9C01E3287E2A}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.Test", "Wd3w.AspNetCore.EasyTesting.Test\Wd3w.AspNetCore.EasyTesting.Test.csproj", "{4CEA31B4-9155-4BCF-B86A-A77068E90BFB}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore", "Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore\Wd3w.AspNetCore.EasyTesting.EntityFrameworkCore.csproj", "{67D8C697-B6DA-4475-A642-D58F8DBFF442}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.Hestify", "Wd3w.AspNetCore.EasyTesting.Hestify\Wd3w.AspNetCore.EasyTesting.Hestify.csproj", "{CBEAFC81-3C74-406A-B80A-1930743BFCA7}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.NSubstitute", "Wd3w.AspNetCore.EasyTesting.NSubstitute\Wd3w.AspNetCore.EasyTesting.NSubstitute.csproj", "{FF10E6B9-93ED-451F-9F7F-F546509D243A}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.Moq", "Wd3w.AspNetCore.EasyTesting.Moq\Wd3w.AspNetCore.EasyTesting.Moq.csproj", "{4984E545-C561-4A4B-B7D5-F29B1FA8F26F}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.FakeItEasy", "Wd3w.AspNetCore.EasyTesting.FakeItEasy\Wd3w.AspNetCore.EasyTesting.FakeItEasy.csproj", "{3979FC93-1A3F-47A8-AA90-AA0874541E6D}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wd3w.AspNetCore.EasyTesting.Grpc", "Wd3w.AspNetCore.EasyTesting.Grpc\Wd3w.AspNetCore.EasyTesting.Grpc.csproj", "{8D88D659-8A11-40E3-A92B-1AC867740D1A}" 28 | EndProject 29 | Global 30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 31 | Debug|Any CPU = Debug|Any CPU 32 | Release|Any CPU = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {BF4866CF-ED28-40DA-B79D-FEFF4138A68C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {BF4866CF-ED28-40DA-B79D-FEFF4138A68C}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {BF4866CF-ED28-40DA-B79D-FEFF4138A68C}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {BF4866CF-ED28-40DA-B79D-FEFF4138A68C}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {09304BAD-484E-4EE9-8A30-9C01E3287E2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {09304BAD-484E-4EE9-8A30-9C01E3287E2A}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {09304BAD-484E-4EE9-8A30-9C01E3287E2A}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {09304BAD-484E-4EE9-8A30-9C01E3287E2A}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {4CEA31B4-9155-4BCF-B86A-A77068E90BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {4CEA31B4-9155-4BCF-B86A-A77068E90BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {4CEA31B4-9155-4BCF-B86A-A77068E90BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {4CEA31B4-9155-4BCF-B86A-A77068E90BFB}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {67D8C697-B6DA-4475-A642-D58F8DBFF442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {67D8C697-B6DA-4475-A642-D58F8DBFF442}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {67D8C697-B6DA-4475-A642-D58F8DBFF442}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {67D8C697-B6DA-4475-A642-D58F8DBFF442}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {CBEAFC81-3C74-406A-B80A-1930743BFCA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {CBEAFC81-3C74-406A-B80A-1930743BFCA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {CBEAFC81-3C74-406A-B80A-1930743BFCA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {CBEAFC81-3C74-406A-B80A-1930743BFCA7}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {FF10E6B9-93ED-451F-9F7F-F546509D243A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {FF10E6B9-93ED-451F-9F7F-F546509D243A}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {FF10E6B9-93ED-451F-9F7F-F546509D243A}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {FF10E6B9-93ED-451F-9F7F-F546509D243A}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {4984E545-C561-4A4B-B7D5-F29B1FA8F26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {4984E545-C561-4A4B-B7D5-F29B1FA8F26F}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {4984E545-C561-4A4B-B7D5-F29B1FA8F26F}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {4984E545-C561-4A4B-B7D5-F29B1FA8F26F}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {3979FC93-1A3F-47A8-AA90-AA0874541E6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {3979FC93-1A3F-47A8-AA90-AA0874541E6D}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {3979FC93-1A3F-47A8-AA90-AA0874541E6D}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {3979FC93-1A3F-47A8-AA90-AA0874541E6D}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {8D88D659-8A11-40E3-A92B-1AC867740D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {8D88D659-8A11-40E3-A92B-1AC867740D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {8D88D659-8A11-40E3-A92B-1AC867740D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {8D88D659-8A11-40E3-A92B-1AC867740D1A}.Release|Any CPU.Build.0 = Release|Any CPU 71 | EndGlobalSection 72 | EndGlobal 73 | -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/Authentication/AuthenticationHandlerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Security.Claims; 4 | using System.Security.Principal; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.DependencyInjection.Extensions; 8 | using Microsoft.Extensions.Options; 9 | 10 | namespace Wd3w.AspNetCore.EasyTesting.Authentication 11 | { 12 | public static class AuthenticationHandlerHelper 13 | { 14 | private static Action GenerateSchemeNameValidator(string schemeName) 15 | { 16 | return options => 17 | { 18 | schemeName ??= options.DefaultScheme; 19 | if (!options.Schemes.Select(s => s.Name).Contains(schemeName)) 20 | throw new InvalidOperationException($"There is no registered scheme({schemeName})."); 21 | }; 22 | } 23 | 24 | public static SystemUnderTest NoUserAuthentication(this SystemUnderTest sut) 25 | { 26 | return sut.FakeAuthentication(AuthenticateResult.NoResult()); 27 | } 28 | 29 | public static SystemUnderTest NoUserAuthentication(this SystemUnderTest sut, string scheme) 30 | { 31 | return sut.FakeAuthentication(scheme, AuthenticateResult.NoResult()); 32 | } 33 | 34 | 35 | public static SystemUnderTest DenyAuthentication(this SystemUnderTest sut, string message = "Failed to authentication") 36 | { 37 | return sut.FakeAuthentication(AuthenticateResult.Fail(message)); 38 | } 39 | 40 | public static SystemUnderTest DenyAuthentication(this SystemUnderTest sut, string scheme, string message = "Failed to authentication") 41 | { 42 | return sut.FakeAuthentication(scheme, AuthenticateResult.Fail(message)); 43 | } 44 | 45 | public static SystemUnderTest DenyAuthentication(this SystemUnderTest sut, Exception exception) 46 | { 47 | return sut.FakeAuthentication(AuthenticateResult.Fail(exception)); 48 | } 49 | 50 | public static SystemUnderTest DenyAuthentication(this SystemUnderTest sut, string scheme, Exception exception) 51 | { 52 | return sut.FakeAuthentication(scheme, AuthenticateResult.Fail(exception)); 53 | } 54 | 55 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, params Claim[] identity) 56 | { 57 | return sut.FakeAuthenticationCore(options => AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(identity, options.DefaultScheme)), options.DefaultScheme))); 58 | } 59 | 60 | 61 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, IIdentity identity) 62 | { 63 | CheckClaimsIsAuthenticated(identity); 64 | return sut.FakeAuthenticationCore(options => AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(identity), options.DefaultScheme))); 65 | } 66 | 67 | 68 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, ClaimsPrincipal pricipal) 69 | { 70 | CheckClaimsIsAuthenticated(pricipal.Identity); 71 | return sut.FakeAuthenticationCore(options => AuthenticateResult.Success(new AuthenticationTicket(pricipal, options.DefaultScheme))); 72 | } 73 | 74 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, string scheme, params Claim[] identity) 75 | { 76 | return sut.FakeAuthentication(scheme, AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(identity, scheme)), scheme))); 77 | } 78 | 79 | 80 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, string scheme, IIdentity identity) 81 | { 82 | CheckClaimsIsAuthenticated(identity); 83 | return sut.FakeAuthentication(scheme, AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(identity), scheme))); 84 | } 85 | 86 | 87 | public static SystemUnderTest AllowAuthentication(this SystemUnderTest sut, string scheme, ClaimsPrincipal pricipal) 88 | { 89 | CheckClaimsIsAuthenticated(pricipal.Identity); 90 | return sut.FakeAuthentication(scheme, AuthenticateResult.Success(new AuthenticationTicket(pricipal, scheme))); 91 | } 92 | 93 | private static void CheckClaimsIsAuthenticated(IIdentity identity) 94 | { 95 | if (!identity.IsAuthenticated) 96 | throw new ArgumentException( 97 | "IIdentity.IsAuthenticated should be true. If you create new instance of ClaimsIdentity, you should create instance with AuthenticationType param of constructor."); 98 | } 99 | 100 | 101 | public static SystemUnderTest FakeAuthentication(this SystemUnderTest sut, AuthenticateResult result) 102 | { 103 | return sut.FakeAuthentication(default, result); 104 | } 105 | 106 | public static SystemUnderTest FakeAuthentication(this SystemUnderTest sut, 107 | string scheme, 108 | AuthenticateResult result) 109 | { 110 | if (result.Succeeded) 111 | CheckClaimsIsAuthenticated(result.Principal.Identity); 112 | 113 | return sut.FakeAuthenticationCore(scheme, 114 | _ => result, 115 | GenerateSchemeNameValidator(scheme)); 116 | } 117 | 118 | public static SystemUnderTest ReplaceAuthenticationHandler(this SystemUnderTest sut, 119 | string scheme, 120 | ServiceLifetime lifetime) 121 | where THandler : IAuthenticationHandler 122 | { 123 | return sut.ReplaceAuthenticationHandler((_, builder) => builder.Name == scheme, 124 | (_, builder) => new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime), 125 | validateOptions: GenerateSchemeNameValidator(scheme)); 126 | } 127 | 128 | public static SystemUnderTest ReplaceAuthenticationHandler(this SystemUnderTest sut, 129 | string scheme, 130 | THandler handler) 131 | where THandler : IAuthenticationHandler 132 | { 133 | return sut.ReplaceAuthenticationHandler((_, builder) => builder.Name == scheme, 134 | (_, builder) => new ServiceDescriptor(typeof(THandler), handler), 135 | validateOptions: GenerateSchemeNameValidator(scheme)); 136 | } 137 | 138 | public static SystemUnderTest ReplaceAuthenticationHandler(this SystemUnderTest sut, 139 | string scheme, 140 | Func factory, 141 | ServiceLifetime lifetime) 142 | where THandler : IAuthenticationHandler 143 | { 144 | return sut.ReplaceAuthenticationHandler((_, builder) => builder.Name == scheme, 145 | (_, builder) => new ServiceDescriptor(typeof(THandler), provider => factory(provider), lifetime), 146 | validateOptions: GenerateSchemeNameValidator(scheme)); 147 | } 148 | 149 | internal static SystemUnderTest FakeAuthenticationCore(this SystemUnderTest sut, Func resultCreator) 150 | { 151 | return sut.FakeAuthenticationCore(default, 152 | options => 153 | { 154 | var authenticateResult = resultCreator(options); 155 | if (authenticateResult.Succeeded) 156 | CheckClaimsIsAuthenticated(authenticateResult.Principal.Identity); 157 | 158 | return authenticateResult; 159 | }); 160 | } 161 | 162 | /// 163 | /// Fake authentication core method 164 | /// 165 | /// 166 | /// 167 | /// 168 | /// 169 | /// 170 | /// 171 | internal static SystemUnderTest FakeAuthenticationCore(this SystemUnderTest sut, 172 | string schemeName, 173 | Func result, 174 | Action validateOptions = default) 175 | { 176 | static Type MakeFakeHandlerType(AuthenticationSchemeBuilder builder) 177 | { 178 | if (IsFakeAuthenticationHandler(builder.HandlerType)) 179 | return builder.HandlerType; 180 | 181 | var baseType = builder 182 | .HandlerType 183 | .BaseType; 184 | 185 | if (baseType!.GetGenericTypeDefinition() != typeof(AuthenticationHandler<>)) 186 | throw new InvalidOperationException("Target scheme authentication handler must inherited AuthenticationHandler"); 187 | 188 | var options = baseType 189 | .GetGenericArguments() 190 | .First(); 191 | return typeof(FakeAuthenticationHandler<>).MakeGenericType(options); 192 | } 193 | 194 | if (sut.ServiceProvider == null) 195 | { 196 | sut.ReplaceAuthenticationHandler((options, builder) => schemeName != null 197 | ? builder.Name == schemeName 198 | : options.DefaultScheme == builder.Name, 199 | (options, builder) => 200 | { 201 | var fakeHandler = MakeFakeHandlerType(builder); 202 | return new ServiceDescriptor(fakeHandler, fakeHandler, ServiceLifetime.Singleton); 203 | }, 204 | (services, type, options) => 205 | { 206 | using var buildInstanceProvider = services.BuildServiceProvider(); 207 | var handler = (IFakeAuthenticationHandler)buildInstanceProvider.GetService(type); 208 | handler.SetResult(result(options)); 209 | services.RemoveAll(type); 210 | services.AddSingleton(type, handler); 211 | }, 212 | validateOptions); 213 | } 214 | else 215 | { 216 | sut.UsingServiceAsync(async provider => 217 | { 218 | var authenticationOptions = provider.GetService>()?.Value; 219 | if (authenticationOptions == null) 220 | throw new InvalidOperationException( 221 | "Couldn't find AuthenticationOptions from SUT. Please configure your own authentication."); 222 | 223 | validateOptions?.Invoke(authenticationOptions); 224 | 225 | var schemeProvider = provider.GetRequiredService(); 226 | var schemes = schemeName == default 227 | ? await schemeProvider.GetDefaultAuthenticateSchemeAsync() 228 | : await schemeProvider.GetSchemeAsync(schemeName); 229 | 230 | if (!IsFakeAuthenticationHandler(schemes.HandlerType)) 231 | throw new InvalidOperationException( 232 | "Couldn't find fake handler. Please call *Authentication before calling Fake/NoUser/Deny/AllowAuthentication"); 233 | var fakeHandler = (IFakeAuthenticationHandler) provider.GetService(schemes.HandlerType); 234 | fakeHandler.SetResult(result(authenticationOptions)); 235 | }).Wait(); 236 | } 237 | return sut; 238 | } 239 | 240 | private static bool IsFakeAuthenticationHandler(Type handlerType) 241 | { 242 | return handlerType.IsGenericType && handlerType.GetGenericTypeDefinition() == typeof(FakeAuthenticationHandler<>); 243 | } 244 | 245 | internal static SystemUnderTest ReplaceAuthenticationHandler(this SystemUnderTest sut, 246 | Func schemePicker, 247 | Func handlerType, 248 | Action postProcess = default, 249 | Action validateOptions = default) 250 | { 251 | sut.CheckClientIsNotCreated(nameof(ReplaceAuthenticationHandler)); 252 | 253 | // see https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication/AuthenticationBuilder.cs 254 | sut.OnConfigureTestServices += services => 255 | { 256 | if (services.All(sd => sd.ServiceType != typeof(IConfigureOptions))) 257 | throw new InvalidOperationException( 258 | "Couldn't find AuthenticationOptions. You should add authentication scheme at least one on your startup class."); 259 | 260 | using var provider = services.BuildServiceProvider(); 261 | var options = provider.GetService>().Value; 262 | 263 | foreach (var builder in options.Schemes) 264 | { 265 | if (!schemePicker(options, builder)) 266 | continue; 267 | validateOptions?.Invoke(options); 268 | services.RemoveAll(builder.HandlerType); 269 | services.Add(handlerType(options, builder)); 270 | } 271 | 272 | services.PostConfigure(options => 273 | { 274 | validateOptions?.Invoke(options); 275 | foreach (var builder in options.Schemes) 276 | { 277 | if (!schemePicker(options, builder)) 278 | continue; 279 | 280 | builder.HandlerType = handlerType(options, builder).ServiceType; 281 | postProcess?.Invoke(services, builder.HandlerType, options); 282 | } 283 | }); 284 | }; 285 | 286 | return sut; 287 | } 288 | } 289 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/Authentication/FakeAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Claims; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Authentication; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Wd3w.AspNetCore.EasyTesting.Authentication 8 | { 9 | public interface IFakeAuthenticationHandler : IAuthenticationHandler 10 | { 11 | void SetSuccess(ClaimsPrincipal claimsPrincipal); 12 | 13 | void SetFail(Exception exception); 14 | 15 | void SetFail(string message); 16 | 17 | void SetResult(AuthenticateResult result); 18 | } 19 | 20 | // Do not remove this. 21 | // ReSharper disable once UnusedTypeParameter 22 | public class FakeAuthenticationHandler : IFakeAuthenticationHandler where TOptions : AuthenticationSchemeOptions, new() 23 | { 24 | private AuthenticateResult _authenticateResult; 25 | 26 | internal HttpContext Context { get; set; } 27 | 28 | internal HttpRequest Request => Context.Request; 29 | 30 | internal HttpResponse Response => Context.Response; 31 | 32 | public AuthenticationScheme Scheme { get; set; } 33 | 34 | public void SetResult(AuthenticateResult result) 35 | { 36 | _authenticateResult = result; 37 | } 38 | 39 | public void SetSuccess(ClaimsPrincipal claimsPrincipal) 40 | { 41 | _authenticateResult = AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name)); 42 | } 43 | 44 | public void SetFail(Exception exception) 45 | { 46 | _authenticateResult = AuthenticateResult.Fail(exception); 47 | } 48 | 49 | public void SetFail(string message) 50 | { 51 | _authenticateResult = AuthenticateResult.Fail(message); 52 | } 53 | 54 | public Task AuthenticateAsync() 55 | { 56 | return Task.FromResult(_authenticateResult); 57 | } 58 | 59 | public Task ChallengeAsync(AuthenticationProperties properties) 60 | { 61 | Response.StatusCode = 401; 62 | return Task.CompletedTask; 63 | } 64 | 65 | public Task ForbidAsync(AuthenticationProperties properties) 66 | { 67 | Response.StatusCode = 403; 68 | return Task.CompletedTask; 69 | } 70 | 71 | public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) 72 | { 73 | Scheme = scheme; 74 | Context = context; 75 | 76 | return Task.CompletedTask; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/HttpResponseMessageAssertionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | using FluentAssertions; 7 | 8 | namespace Wd3w.AspNetCore.EasyTesting 9 | { 10 | public static class HttpResponseMessageAssertionHelper 11 | { 12 | public static JsonSerializerOptions JsonSerializerOptions { get; set; } = new JsonSerializerOptions 13 | { 14 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase 15 | }; 16 | 17 | /// 18 | /// Verify response message status code is ok. 19 | /// 20 | /// 21 | public static void ShouldBeOk(this HttpResponseMessage message) 22 | { 23 | message.ShouldBe(HttpStatusCode.OK); 24 | } 25 | 26 | /// 27 | /// Verify response message status code is ok and deserialization json as TResponse type. 28 | /// 29 | /// 30 | /// 31 | /// 32 | public static async Task ShouldBeOk(this HttpResponseMessage message) 33 | { 34 | return await message.ShouldBeCodeWithBody(HttpStatusCode.OK); 35 | } 36 | 37 | /// 38 | /// Verify response message status code is ok and deserialization response json as TResponse and provide to assertion action. 39 | /// 40 | /// 41 | /// 42 | /// 43 | /// 44 | public static async Task ShouldBeOk(this HttpResponseMessage message, Action assertion) 45 | { 46 | assertion(await message.ShouldBeOk()); 47 | } 48 | 49 | /// 50 | /// Verify response code is nocontent. 51 | /// 52 | /// 53 | public static void ShouldBeNoContent(this HttpResponseMessage message) 54 | { 55 | message.ShouldBe(HttpStatusCode.NoContent); 56 | } 57 | 58 | /// 59 | /// Verify response code is accepted. 60 | /// 61 | /// 62 | public static void ShouldBeAccepted(this HttpResponseMessage message) 63 | { 64 | message.ShouldBe(HttpStatusCode.Accepted); 65 | } 66 | 67 | /// 68 | /// Verify response code is bad request. 69 | /// 70 | /// 71 | public static void ShouldBeBadRequest(this HttpResponseMessage message) 72 | { 73 | message.ShouldBe(HttpStatusCode.BadRequest); 74 | } 75 | 76 | /// 77 | /// Verify response code is forbidden. 78 | /// 79 | /// 80 | public static void ShouldBeForbidden(this HttpResponseMessage message) 81 | { 82 | message.ShouldBe(HttpStatusCode.Forbidden); 83 | } 84 | 85 | /// 86 | /// Verify response code. 87 | /// 88 | /// 89 | /// 90 | public static void ShouldBe(this HttpResponseMessage message, HttpStatusCode code) 91 | { 92 | message.StatusCode.Should().Be(code); 93 | } 94 | 95 | /// 96 | /// Verify response code and deserialize json response. 97 | /// 98 | /// 99 | /// 100 | /// 101 | /// 102 | public static async Task ShouldBeCodeWithBody(this HttpResponseMessage message, 103 | HttpStatusCode code) 104 | { 105 | message.ShouldBe(code); 106 | return await message.ReadJsonBodyAsync(); 107 | } 108 | 109 | /// 110 | /// Deserialize json body content from response message. 111 | /// 112 | /// 113 | /// 114 | /// 115 | public static async Task ReadJsonBodyAsync(this HttpResponseMessage message) 116 | { 117 | var body = await message.Content.ReadAsStringAsync(); 118 | return JsonSerializer.Deserialize(body, JsonSerializerOptions); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/Internal/ServiceCollectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Wd3w.AspNetCore.EasyTesting.Internal 5 | { 6 | internal static class ServiceCollectionHelper 7 | { 8 | internal static ServiceDescriptor FindServiceDescriptor(this IServiceCollection services) 9 | { 10 | return services.First(d => d.ServiceType == typeof(TService)); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/SystemUnderTest.Verify.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentAssertions; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting 7 | { 8 | public abstract partial class SystemUnderTest 9 | { 10 | /// 11 | /// Verify lifetime of registered service. 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public void VerifyRegisteredLifeTimeOfService(ServiceLifetime lifetime) 18 | { 19 | CheckServiceCollectionAllocated(); 20 | FindServiceDescriptor().Lifetime.Should().Be(lifetime); 21 | } 22 | 23 | /// 24 | /// Verify implementation type of registered service. 25 | /// 26 | /// 27 | /// 28 | public void VerifyRegisteredImplementationTypeOfService() 29 | { 30 | CheckServiceCollectionAllocated(); 31 | GetImplementationType(FindServiceDescriptor()).Should().Be(typeof(TImplementation)); 32 | } 33 | 34 | /// 35 | /// Verify service collection with custom condition expression 36 | /// 37 | /// 38 | public void VerifyRegistrationByCondition(Expression> condition) 39 | { 40 | CheckServiceCollectionAllocated(); 41 | _serviceCollection.Should().Contain(condition); 42 | } 43 | 44 | /// 45 | /// Verify there are no service descriptor registration by condition expression. 46 | /// 47 | /// 48 | public void VerifyNoRegistrationByCondition(Expression> condition) 49 | { 50 | CheckServiceCollectionAllocated(); 51 | _serviceCollection.Should().NotContain(condition); 52 | } 53 | 54 | /// 55 | /// Verify there are no service descriptor of TService type 56 | /// 57 | /// 58 | public void VerifyNoRegistration() 59 | { 60 | VerifyNoRegistration(typeof(TService)); 61 | } 62 | 63 | /// 64 | /// Verify there are no service descriptor of serviceType 65 | /// 66 | /// 67 | public void VerifyNoRegistration(Type serviceType) 68 | { 69 | CheckServiceCollectionAllocated(); 70 | _serviceCollection.Should().NotContain(descriptor => descriptor.ServiceType == serviceType); 71 | } 72 | 73 | /// 74 | /// Verify there are no service descriptor of TService type and TImplementation type 75 | /// 76 | /// 77 | /// 78 | public void VerifyNoRegistration() where TImplementation : class, TService 79 | { 80 | VerifyNoRegistration(typeof(TService), typeof(TImplementation)); 81 | } 82 | 83 | 84 | /// 85 | /// Verify there are no service descriptor of serviceType and implementationType 86 | /// 87 | /// 88 | /// 89 | public void VerifyNoRegistration(Type serviceType, Type implementationType) 90 | { 91 | CheckServiceCollectionAllocated(); 92 | _serviceCollection.Should().NotContain(descriptor => 93 | descriptor.ServiceType == serviceType && (descriptor.ImplementationType == implementationType || 94 | descriptor.ImplementationInstance.GetType() == implementationType)); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/SystemUnderTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using Microsoft.AspNetCore.Mvc.Testing; 4 | using Microsoft.AspNetCore.TestHost; 5 | 6 | namespace Wd3w.AspNetCore.EasyTesting 7 | { 8 | public class SystemUnderTest : SystemUnderTest where TStartup : class 9 | { 10 | private readonly WebApplicationFactory _factory; 11 | 12 | public SystemUnderTest() 13 | { 14 | _factory = new WebApplicationFactory(); 15 | } 16 | 17 | public SystemUnderTest(WebApplicationFactory factory) 18 | { 19 | _factory = factory; 20 | } 21 | 22 | public override void Dispose() 23 | { 24 | _factory?.Dispose(); 25 | } 26 | 27 | public override HttpClient CreateClient() 28 | { 29 | return WithWebHostBuilder(builder => builder.CreateClient()); 30 | } 31 | 32 | public override HttpClient CreateClient(WebApplicationFactoryClientOptions options) 33 | { 34 | return WithWebHostBuilder(builder => builder.CreateClient(options)); 35 | } 36 | 37 | public override HttpClient CreateDefaultClient(params DelegatingHandler[] handlers) 38 | { 39 | return WithWebHostBuilder(builder => builder.CreateDefaultClient(handlers)); 40 | } 41 | 42 | public override HttpClient CreateDefaultClient(Uri baseAddress, params DelegatingHandler[] handlers) 43 | { 44 | return WithWebHostBuilder(builder => builder.CreateDefaultClient(baseAddress, handlers)); 45 | } 46 | 47 | private WebApplicationFactory WithWebHostBuilder() 48 | { 49 | return _factory.WithWebHostBuilder(ConfigureWebHostBuilder); 50 | } 51 | 52 | private HttpClient WithWebHostBuilder(Func, HttpClient> createClient) 53 | { 54 | var builder = WithWebHostBuilder(); 55 | var httpClient = createClient(builder); 56 | ServiceProvider = builder.Services; 57 | ExecuteSetupFixture(); 58 | return httpClient; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/SystemUnderTestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Runtime.CompilerServices; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.AspNetCore.Mvc.Testing; 11 | using Microsoft.AspNetCore.TestHost; 12 | using Microsoft.Extensions.Caching.Distributed; 13 | using Microsoft.Extensions.Caching.Memory; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.DependencyInjection.Extensions; 17 | using Microsoft.Extensions.Hosting; 18 | using Microsoft.Extensions.Logging; 19 | using Microsoft.Extensions.Options; 20 | using Wd3w.AspNetCore.EasyTesting.Internal; 21 | 22 | [assembly: InternalsVisibleTo("Wd3w.AspNetCore.EasyTesting.NSubstitute")] 23 | [assembly: InternalsVisibleTo("Wd3w.AspNetCore.EasyTesting.Moq")] 24 | [assembly: InternalsVisibleTo("Wd3w.AspNetCore.EasyTesting.FakeItEasy")] 25 | [assembly: InternalsVisibleTo("Wd3w.AspNetCore.EasyTesting.Test")] 26 | 27 | namespace Wd3w.AspNetCore.EasyTesting 28 | { 29 | public abstract partial class SystemUnderTest : IDisposable 30 | { 31 | private IServiceCollection _serviceCollection; 32 | 33 | public IServiceProvider ServiceProvider { get; protected set; } 34 | 35 | internal IServiceCollection InternalServiceCollection { get; } = new ServiceCollection(); 36 | 37 | internal IServiceProvider InternalServiceProvider => InternalServiceCollection.BuildServiceProvider(); 38 | 39 | public abstract void Dispose(); 40 | 41 | internal event SetupFixtureHandler OnSetupFixtures; 42 | 43 | internal event ConfigureTestServiceHandler OnConfigureTestServices; 44 | 45 | internal event ConfigureWebHostBuilderHandler OnConfigureWebHostBuilder; 46 | 47 | internal void CheckClientIsNotCreated(string methodName) 48 | { 49 | if (ServiceProvider != default) 50 | throw new InvalidOperationException($"{methodName} can be using before calling CreateClient/Create* methods."); 51 | } 52 | 53 | internal SystemUnderTest CheckClientIsNotCreated(string methodName, Action action) 54 | { 55 | CheckClientIsNotCreated(methodName); 56 | action(); 57 | return this; 58 | } 59 | 60 | internal SystemUnderTest CheckClientIsNotCreated(string methodName, SetupFixtureHandler action) 61 | { 62 | CheckClientIsNotCreated(methodName); 63 | OnSetupFixtures += action; 64 | return this; 65 | } 66 | 67 | internal SystemUnderTest CheckClientIsNotCreatedAndConfigureServices(string methodName, ConfigureTestServiceHandler action) 68 | { 69 | CheckClientIsNotCreated(methodName); 70 | OnConfigureTestServices += action; 71 | return this; 72 | } 73 | 74 | internal SystemUnderTest CheckClientIsNotCreated(string methodName, ConfigureWebHostBuilderHandler action) 75 | { 76 | CheckClientIsNotCreated(methodName); 77 | OnConfigureWebHostBuilder += action; 78 | return this; 79 | } 80 | 81 | internal void CheckClientIsCreated(string methodName) 82 | { 83 | if (ServiceProvider == default) throw new InvalidOperationException($"{methodName} can be using after calling CreateClient/Create& methods."); 84 | } 85 | 86 | /// 87 | /// Replace pre-registered service with TImplementation type with lifetime parameter. 88 | /// 89 | /// If lifetime is null, pre-register service lifetime will be used. 90 | /// 91 | /// 92 | /// 93 | public SystemUnderTest ReplaceService(ServiceLifetime? lifetime = default) 94 | { 95 | CheckClientIsNotCreated(nameof(ReplaceService)); 96 | OnConfigureTestServices += services => 97 | { 98 | var descriptor = services.FindServiceDescriptor(); 99 | services.Replace(new ServiceDescriptor(typeof(TService), typeof(TImplementation), 100 | lifetime ?? descriptor.Lifetime)); 101 | }; 102 | return this; 103 | } 104 | 105 | /// 106 | /// Replace logger providers with new logger factory 107 | /// 108 | /// 109 | /// 110 | public SystemUnderTest ReplaceLoggerFactory(params ILoggerProvider[] providers) 111 | { 112 | CheckClientIsNotCreated(nameof(ReplaceLoggerFactory)); 113 | ReplaceService(new LoggerFactory(providers)); 114 | return this; 115 | } 116 | 117 | /// 118 | /// Replace logger factory with logger builder 119 | /// 120 | /// 121 | /// 122 | public SystemUnderTest ReplaceLoggerFactory(Action configure) 123 | { 124 | CheckClientIsNotCreated(nameof(ReplaceLoggerFactory)); 125 | ReplaceService(LoggerFactory.Create(configure)); 126 | return this; 127 | } 128 | 129 | 130 | /// 131 | /// Replace distributed cache to in-memory cache for testing. 132 | /// 133 | /// 134 | /// 135 | public SystemUnderTest ReplaceDistributedInMemoryCache(MemoryDistributedCacheOptions options = default) 136 | { 137 | ReplaceService(new MemoryDistributedCache( 138 | new OptionsWrapper(options ?? new MemoryDistributedCacheOptions()))); 139 | return this; 140 | } 141 | 142 | /// 143 | /// Replace pre-registered service with parameter object, the obj will register as singleton. 144 | /// 145 | /// 146 | /// 147 | /// 148 | public SystemUnderTest ReplaceService(TService obj) 149 | { 150 | CheckClientIsNotCreated(nameof(ReplaceService)); 151 | OnConfigureTestServices += services => services.Replace(new ServiceDescriptor(typeof(TService), obj)); 152 | return this; 153 | } 154 | 155 | /// 156 | /// Replace registered configure options. 157 | /// 158 | /// Option configurer 159 | /// 160 | /// 161 | public SystemUnderTest ReplaceConfigureOptions(Action configurer) where TOptions : class 162 | { 163 | CheckClientIsNotCreated(nameof(ReplaceConfigureOptions)); 164 | OnConfigureTestServices += services => 165 | { 166 | services.RemoveAll>(); 167 | services.AddSingleton>(new ConfigureOptions(configurer)); 168 | }; 169 | return this; 170 | } 171 | 172 | /// 173 | /// Remove all TService service registrations. 174 | /// 175 | /// 176 | /// 177 | public SystemUnderTest RemoveAll() 178 | { 179 | return CheckClientIsNotCreatedAndConfigureServices(nameof(RemoveAll), services => services.RemoveAll()); 180 | } 181 | 182 | /// 183 | /// Remove all TService service registrations. 184 | /// 185 | /// 186 | /// 187 | public SystemUnderTest RemoveAll(Type serviceType) 188 | { 189 | return CheckClientIsNotCreatedAndConfigureServices(nameof(RemoveAll), services => services.RemoveAll(serviceType)); 190 | } 191 | 192 | /// 193 | /// Remove TImplementation of TService registrations. 194 | /// 195 | /// 196 | /// 197 | /// 198 | public SystemUnderTest Remove() 199 | { 200 | return Remove(typeof(TService), typeof(TImplementation)); 201 | } 202 | 203 | /// 204 | /// Remove implementationType of serviceType registrations. 205 | /// 206 | /// 207 | /// 208 | /// 209 | public SystemUnderTest Remove(Type serviceType, Type implementationType) 210 | { 211 | return CheckClientIsNotCreatedAndConfigureServices(nameof(Remove), services => 212 | { 213 | var descriptor = services.FirstOrDefault(registration => 214 | registration.ServiceType == serviceType && 215 | registration.ImplementationType == implementationType); 216 | if (descriptor == default) return; 217 | services.Remove(descriptor); 218 | }); 219 | } 220 | 221 | /// 222 | /// Remove registration by condition expression. 223 | /// 224 | /// 225 | /// 226 | public SystemUnderTest RemoveAllBy(Func condition) 227 | { 228 | return CheckClientIsNotCreatedAndConfigureServices(nameof(RemoveAllBy), services => 229 | { 230 | foreach (var descriptor in services.Where(condition).ToArray()) 231 | { 232 | services.Remove(descriptor); 233 | } 234 | }); 235 | } 236 | 237 | /// 238 | /// Remove only one registration by condition. 239 | /// 240 | /// 241 | /// 242 | public SystemUnderTest RemoveSingleBy(Func condition) 243 | { 244 | return CheckClientIsNotCreatedAndConfigureServices(nameof(RemoveSingleBy), services => services.Remove(services.Single(condition))); 245 | } 246 | 247 | /// 248 | /// Remove all startup filters. 249 | /// 250 | /// 251 | public SystemUnderTest DisableStartupFilters() 252 | { 253 | return CheckClientIsNotCreatedAndConfigureServices(nameof(DisableStartupFilters), services => services.RemoveAll()); 254 | } 255 | 256 | /// 257 | /// Remove specific startup filter for TImplementationFilter 258 | /// 259 | /// 260 | /// 261 | public SystemUnderTest DisableStartupFilter() 262 | where TImplementationFilter : IStartupFilter 263 | { 264 | var filterType = typeof(TImplementationFilter); 265 | return CheckClientIsNotCreatedAndConfigureServices(nameof(DisableStartupFilter), services => 266 | { 267 | var registration = services.First(descriptor => descriptor.ImplementationType == filterType || descriptor.ImplementationInstance?.GetType() == filterType); 268 | services.Remove(registration); 269 | }); 270 | } 271 | 272 | /// 273 | /// Replace registered named options configurer 274 | /// 275 | /// Option name 276 | /// 277 | /// 278 | /// 279 | public SystemUnderTest ReplaceNamedConfigureOptions(string name, Action configurer) where TOptions : class 280 | { 281 | CheckClientIsNotCreated(nameof(ReplaceConfigureOptions)); 282 | OnConfigureTestServices += services => 283 | { 284 | foreach (var remove in services.Where(descriptor => descriptor.ServiceType == typeof(IConfigureOptions)) 285 | .Where(descriptor => descriptor.ImplementationInstance is ConfigureNamedOptions configure && configure.Name == name) 286 | .ToList()) 287 | { 288 | services.Remove(remove); 289 | } 290 | 291 | services.AddSingleton>(new ConfigureNamedOptions(name, configurer)); 292 | }; 293 | return this; 294 | } 295 | 296 | /// 297 | /// Remove registered IValidateOptions for TOptions 298 | /// 299 | /// Option class 300 | /// 301 | public SystemUnderTest DisableOptionValidations() where TOptions : class 302 | { 303 | CheckClientIsNotCreated(nameof(DisableOptionValidations)); 304 | OnConfigureTestServices += services => services.RemoveAll>(); 305 | return this; 306 | } 307 | 308 | /// 309 | /// Remove data annotation validator only for TOption 310 | /// 311 | /// The option class 312 | /// 313 | public SystemUnderTest DisableOptionDataAnnotationValidation() where TOptions : class 314 | { 315 | CheckClientIsNotCreated(nameof(DisableOptionDataAnnotationValidation)); 316 | OnConfigureTestServices += services => 317 | { 318 | var serviceDescriptor = services.FirstOrDefault(descriptor => 319 | descriptor.ServiceType == typeof(IValidateOptions) && 320 | descriptor.ImplementationInstance != null && 321 | descriptor.ImplementationInstance.GetType() == typeof(DataAnnotationValidateOptions)); 322 | 323 | if (serviceDescriptor != default) 324 | services.Remove(serviceDescriptor); 325 | }; 326 | return this; 327 | } 328 | 329 | internal TService GetOrAddInternalService(Func factory) where TService : class 330 | { 331 | InternalServiceCollection.TryAddSingleton(factory()); 332 | return InternalServiceProvider.GetRequiredService(); 333 | } 334 | 335 | /// 336 | /// Add fixture setting hook, this hook will be called once after register every system or test services. 337 | /// 338 | /// 339 | /// 340 | /// 341 | public SystemUnderTest SetupFixture(Func action) 342 | { 343 | CheckClientIsNotCreated(nameof(SetupFixture)); 344 | OnSetupFixtures += provider => action.Invoke(provider.GetService()); 345 | return this; 346 | } 347 | 348 | /// 349 | /// Use services from internal service providers 350 | /// 351 | /// 352 | /// 353 | public async Task UsingServiceAsync(Func action) 354 | { 355 | CheckClientIsCreated(nameof(UsingServiceAsync)); 356 | using var scope = ServiceProvider.CreateScope(); 357 | await action.Invoke(scope.ServiceProvider); 358 | } 359 | 360 | /// 361 | /// Use TService object from system service provider. 362 | /// 363 | /// 364 | /// 365 | /// 366 | public async Task UsingServiceAsync(Func action) 367 | { 368 | CheckClientIsCreated(nameof(UsingServiceAsync)); 369 | using var scope = ServiceProvider.CreateScope(); 370 | await action.Invoke(scope.ServiceProvider.GetService()); 371 | } 372 | 373 | /// 374 | /// Use two service objects from system service provider. 375 | /// 376 | /// 377 | /// 378 | /// 379 | /// 380 | public async Task UsingServiceAsync(Func action) 381 | { 382 | CheckClientIsCreated(nameof(UsingServiceAsync)); 383 | using var scope = ServiceProvider.CreateScope(); 384 | await action.Invoke(scope.ServiceProvider.GetService(), 385 | scope.ServiceProvider.GetService()); 386 | } 387 | 388 | /// 389 | /// Use three service objects from system service provider. 390 | /// 391 | /// 392 | /// 393 | /// 394 | /// 395 | /// 396 | public async Task UsingServiceAsync( 397 | Func action) 398 | { 399 | CheckClientIsCreated(nameof(UsingServiceAsync)); 400 | using var scope = ServiceProvider.CreateScope(); 401 | var provider = scope.ServiceProvider; 402 | await action.Invoke(provider.GetService(), provider.GetService(), 403 | provider.GetService()); 404 | } 405 | 406 | /// 407 | /// Use service provider. 408 | /// 409 | /// 410 | public void UsingService(Action action) 411 | { 412 | CheckClientIsCreated(nameof(UsingService)); 413 | using var scope = ServiceProvider.CreateScope(); 414 | action.Invoke(scope.ServiceProvider); 415 | } 416 | 417 | /// 418 | /// Use TService object from system service provider. 419 | /// 420 | /// 421 | /// 422 | public void UsingService(Action action) 423 | { 424 | CheckClientIsCreated(nameof(UsingService)); 425 | using var scope = ServiceProvider.CreateScope(); 426 | action.Invoke(scope.ServiceProvider.GetService()); 427 | } 428 | 429 | /// 430 | /// Use two service objects from system service provider. 431 | /// 432 | /// 433 | /// 434 | /// 435 | public void UsingService(Action action) 436 | { 437 | CheckClientIsCreated(nameof(UsingService)); 438 | using var scope = ServiceProvider.CreateScope(); 439 | var provider = scope.ServiceProvider; 440 | action.Invoke(provider.GetService(), provider.GetService()); 441 | } 442 | 443 | /// 444 | /// Use three service objects from system service provider. 445 | /// 446 | /// 447 | /// 448 | /// 449 | /// 450 | public void UsingService(Action action) 451 | { 452 | CheckClientIsCreated(nameof(UsingService)); 453 | using var scope = ServiceProvider.CreateScope(); 454 | var provider = scope.ServiceProvider; 455 | action.Invoke(provider.GetService(), ServiceProvider.GetService(), 456 | ServiceProvider.GetService()); 457 | } 458 | 459 | private ServiceDescriptor FindServiceDescriptor() 460 | { 461 | return _serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(TService)) 462 | ?? throw new InvalidOperationException( 463 | "The provided service type is not registered from SUT service collection."); 464 | } 465 | 466 | private Type GetImplementationType(ServiceDescriptor descriptor) 467 | { 468 | if (descriptor.ImplementationType != default) 469 | return descriptor.ImplementationType; 470 | 471 | if (descriptor.ImplementationInstance != default) 472 | return descriptor.ImplementationInstance.GetType(); 473 | 474 | 475 | using var provider = _serviceCollection.BuildServiceProvider(); 476 | using var serviceScope = provider.CreateScope(); 477 | var implementationTypeObject = serviceScope.ServiceProvider.GetService(descriptor.ServiceType); 478 | if (implementationTypeObject == null) 479 | throw new InvalidOperationException( 480 | "Can not get type of service type from implementation factory of service descriptor in service collection."); 481 | 482 | return implementationTypeObject.GetType(); 483 | } 484 | 485 | private void CheckServiceCollectionAllocated() 486 | { 487 | if (_serviceCollection == default) 488 | throw new InvalidOperationException( 489 | "Should create client before verify service's registered lifetime."); 490 | } 491 | 492 | /// 493 | /// Setup configure web host builder. 494 | /// 495 | /// 496 | /// 497 | public SystemUnderTest SetupWebHostBuilder(Action configureAction) 498 | { 499 | CheckClientIsNotCreated(nameof(SetupWebHostBuilder)); 500 | OnConfigureWebHostBuilder += configureAction.Invoke; 501 | return this; 502 | } 503 | 504 | /// 505 | /// UseEnvironment delegate method. 506 | /// 507 | /// Specific environment for starting host 508 | /// 509 | public SystemUnderTest UseEnvironment(string environment) 510 | { 511 | CheckClientIsNotCreated(nameof(UseEnvironment)); 512 | OnConfigureWebHostBuilder += builder => builder.UseEnvironment(environment); 513 | return this; 514 | } 515 | 516 | /// 517 | /// Use Production environment when host is started. 518 | /// 519 | /// 520 | public SystemUnderTest UseProductionEnvironment() 521 | { 522 | CheckClientIsNotCreated(nameof(UseProductionEnvironment)); 523 | OnConfigureWebHostBuilder += builder => builder.UseEnvironment(Environments.Production); 524 | return this; 525 | } 526 | 527 | /// 528 | /// Use Staging environment when host is started. 529 | /// 530 | /// 531 | public SystemUnderTest UseStagingEnvironment() 532 | { 533 | CheckClientIsNotCreated(nameof(UseStagingEnvironment)); 534 | OnConfigureWebHostBuilder += builder => builder.UseEnvironment(Environments.Staging); 535 | return this; 536 | } 537 | 538 | /// 539 | /// Use Development environment when host is started. 540 | /// 541 | /// 542 | public SystemUnderTest UseDevelopmentEnvironment() 543 | { 544 | CheckClientIsNotCreated(nameof(UseDevelopmentEnvironment)); 545 | OnConfigureWebHostBuilder += builder => builder.UseEnvironment(Environments.Development); 546 | return this; 547 | } 548 | 549 | /// 550 | /// Add setting for sut 551 | /// 552 | /// 553 | /// 554 | /// 555 | public SystemUnderTest UseSetting(string key, string value) 556 | { 557 | CheckClientIsNotCreated(nameof(UseSetting)); 558 | OnConfigureWebHostBuilder += builder => builder.UseSetting(key, value); 559 | return this; 560 | } 561 | 562 | /// 563 | /// Hook for configuring application 564 | /// 565 | /// 566 | /// 567 | public SystemUnderTest ConfigureAppConfiguration(Action configureAction) 568 | { 569 | CheckClientIsNotCreated(nameof(ConfigureAppConfiguration)); 570 | OnConfigureWebHostBuilder += builder => builder.ConfigureAppConfiguration(configureAction); 571 | return this; 572 | } 573 | 574 | /// 575 | /// Override app configuration members with options instance 576 | /// 577 | /// 578 | /// 579 | /// 580 | public SystemUnderTest OverrideAppConfiguration(T options) where T : class 581 | { 582 | CheckClientIsNotCreated(nameof(OverrideAppConfiguration)); 583 | var json = JsonSerializer.SerializeToUtf8Bytes(options); 584 | ConfigureAppConfiguration(builder => builder.AddJsonStream(new MemoryStream(json))); 585 | return this; 586 | } 587 | 588 | /// 589 | /// Override app configuration members with dynamic instance 590 | /// 591 | /// 592 | /// 593 | public SystemUnderTest OverrideAppConfiguration(dynamic options) 594 | { 595 | CheckClientIsNotCreated(nameof(OverrideAppConfiguration)); 596 | var json = JsonSerializer.SerializeToUtf8Bytes(options); 597 | ConfigureAppConfiguration(builder => builder.AddJsonStream(new MemoryStream(json))); 598 | return this; 599 | } 600 | 601 | /// 602 | /// Override app configuration members with path 603 | /// 604 | /// 605 | /// 606 | /// 607 | public SystemUnderTest OverrideAppConfiguration(string configurationPath, string value) 608 | { 609 | CheckClientIsNotCreated(nameof(OverrideAppConfiguration)); 610 | ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(new []{new KeyValuePair(configurationPath, value)})); 611 | return this; 612 | } 613 | 614 | /// 615 | /// Override app configuration members with key value collection 616 | /// 617 | /// 618 | /// 619 | public SystemUnderTest OverrideAppConfiguration(IDictionary collection) 620 | { 621 | CheckClientIsNotCreated(nameof(OverrideAppConfiguration)); 622 | ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(collection)); 623 | return this; 624 | } 625 | 626 | /// 627 | /// Hook for configuring services. 628 | /// 629 | /// 630 | /// 631 | public SystemUnderTest ConfigureServices(Action configureServices) 632 | { 633 | OnConfigureWebHostBuilder += builder => builder.ConfigureServices(configureServices); 634 | return this; 635 | } 636 | 637 | internal void ConfigureWebHostBuilder(IWebHostBuilder builder) 638 | { 639 | OnConfigureWebHostBuilder?.Invoke(builder); 640 | builder.ConfigureTestServices(services => 641 | { 642 | OnConfigureTestServices?.Invoke(services); 643 | _serviceCollection = services; 644 | }); 645 | } 646 | 647 | internal void ExecuteSetupFixture() 648 | { 649 | if (OnSetupFixtures == null) return; 650 | 651 | using var scope = ServiceProvider.CreateScope(); 652 | Task.WhenAll(OnSetupFixtures 653 | .GetInvocationList() 654 | .Cast() 655 | .Select(handler => handler(scope.ServiceProvider))) 656 | .Wait(); 657 | } 658 | 659 | /// 660 | /// Create http client to access system under test. 661 | /// 662 | /// HttpClient 663 | public abstract HttpClient CreateClient(); 664 | 665 | /// 666 | /// Create http client to access system under test with options. 667 | /// 668 | /// FactoryOptions 669 | /// HttpClient 670 | public abstract HttpClient CreateClient(WebApplicationFactoryClientOptions options); 671 | 672 | /// 673 | /// Create http default client with delegate handlers. 674 | /// 675 | /// 676 | /// 677 | public abstract HttpClient CreateDefaultClient(params DelegatingHandler[] handlers); 678 | 679 | /// 680 | /// Create http default client with uri and delegate handlers. 681 | /// 682 | /// 683 | /// 684 | /// 685 | public abstract HttpClient CreateDefaultClient(Uri baseAddress, params DelegatingHandler[] handlers); 686 | 687 | internal delegate void ConfigureTestServiceHandler(IServiceCollection services); 688 | 689 | internal delegate Task SetupFixtureHandler(IServiceProvider provider); 690 | 691 | internal delegate void ConfigureWebHostBuilderHandler(IWebHostBuilder builder); 692 | } 693 | } -------------------------------------------------------------------------------- /src/Wd3w.AspNetCore.EasyTesting/Wd3w.AspNetCore.EasyTesting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PackageIdBase) 4 | bin\Debug\Wd3w.AspNetCore.EasyTesting.xml 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------