├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── auto-assign.yml │ ├── codeql-analysis.yml │ └── stale.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── _config.yml ├── azure-pipeline-PR.yml ├── azure-pipelines.yml ├── examples └── Xunit.Microsoft.DependencyInjection.ExampleTests │ ├── CalculatorTests.cs │ ├── Fixtures │ └── TestProjectFixture.cs │ ├── GlobalUsings.cs │ ├── KeyedServicesTests.cs │ ├── SecretValues.cs │ ├── Services │ ├── Calculator.cs │ ├── ICalculator.cs │ ├── ICarMaker.cs │ ├── Options.cs │ ├── Porsche.cs │ └── Toyota.cs │ ├── UnitTests.cs │ ├── UserSecretTests.cs │ ├── Xunit.Microsoft.DependencyInjection.ExampleTests.csproj │ └── appsettings.json ├── google1399ddbab0b8eed5.html └── src ├── .editorconfig ├── Abstracts ├── TestBed.cs └── TestBedFixture.cs ├── Attributes └── TestOrderAttribute.cs ├── GlobalUsings.cs ├── Logging ├── NilLoggerProvider.cs ├── NoOpDisposable.cs ├── OutputLogger.cs └── OutputLoggerProvider.cs ├── TestAppSettings.cs ├── TestsOrder └── TestPriorityOrderer.cs ├── Xunit.Microsoft.DependencyInjection.csproj ├── Xunit.Microsoft.DependencyInjection.sln └── package.nuspec /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | Arash-Sabet 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Umplify # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **dotnet core version info:** 27 | Please run ```dotnet --info``` and copy/paste the results here. 28 | 29 | **Operation system:** 30 | 1. Windows 10 31 | 2. macOS Big Sur 32 | 3. Ubuntu 33 | 4. etc. 34 | 35 | **Additional context** 36 | Add any other context about the problem here if applicable. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/auto-assign.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign 2 | on: 3 | issues: 4 | types: [opened] 5 | pull_request: 6 | types: [opened] 7 | jobs: 8 | run: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - name: 'Auto-assign issue' 15 | uses: pozil/auto-assign-issue@v1 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | assignees: Arash-Sabet 19 | numOfAssignee: 1 20 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # ******** NOTE ******** 12 | 13 | name: "CodeQL" 14 | 15 | on: 16 | push: 17 | branches: [ main ] 18 | pull_request: 19 | # The branches below must be a subset of the branches above 20 | branches: [ main ] 21 | schedule: 22 | - cron: '42 23 * * 4' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | language: [ 'csharp' ] 33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 34 | # Learn more: 35 | # https://docs.github.com/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v2 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v1 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 50 | 51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 52 | # If this step fails, then you should remove it and run the build manually (see below) 53 | - name: Autobuild 54 | uses: github/codeql-action/autobuild@v1 55 | 56 | # ℹ️ Command-line programs to run using the OS shell. 57 | # 📚 https://git.io/JvXDl 58 | 59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 60 | # and modify them (or add more) to build your code if your project 61 | # uses a compiled language 62 | 63 | #- run: | 64 | # make bootstrap 65 | # make release 66 | 67 | - name: Perform CodeQL Analysis 68 | uses: github/codeql-action/analyze@v1 69 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "30 1 * * *" 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/stale@v1 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | stale-issue-message: 'Stale issue message' 17 | stale-pr-message: 'Stale pull request message' 18 | stale-issue-label: 'no-issue-activity' 19 | stale-pr-label: 'no-pr-activity' 20 | -------------------------------------------------------------------------------- /.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 | *.idea 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Mono auto generated files 18 | mono_crash.* 19 | 20 | # Build results 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Rr]elease/ 24 | [Rr]eleases/ 25 | x64/ 26 | x86/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # StyleCop 66 | StyleCopReport.xml 67 | 68 | # Files built by Visual Studio 69 | *_i.c 70 | *_p.c 71 | *_h.h 72 | *.ilk 73 | *.meta 74 | *.obj 75 | *.iobj 76 | *.pch 77 | *.pdb 78 | *.ipdb 79 | *.pgc 80 | *.pgd 81 | *.rsp 82 | *.sbr 83 | *.tlb 84 | *.tli 85 | *.tlh 86 | *.tmp 87 | *.tmp_proj 88 | *_wpftmp.csproj 89 | *.log 90 | *.vspscc 91 | *.vssscc 92 | .builds 93 | *.pidb 94 | *.svclog 95 | *.scc 96 | 97 | # Chutzpah Test files 98 | _Chutzpah* 99 | 100 | # Visual C++ cache files 101 | ipch/ 102 | *.aps 103 | *.ncb 104 | *.opendb 105 | *.opensdf 106 | *.sdf 107 | *.cachefile 108 | *.VC.db 109 | *.VC.VC.opendb 110 | 111 | # Visual Studio profiler 112 | *.psess 113 | *.vsp 114 | *.vspx 115 | *.sap 116 | 117 | # Visual Studio Trace Files 118 | *.e2e 119 | 120 | # TFS 2012 Local Workspace 121 | $tf/ 122 | 123 | # Guidance Automation Toolkit 124 | *.gpState 125 | 126 | # ReSharper is a .NET coding add-in 127 | _ReSharper*/ 128 | *.[Rr]e[Ss]harper 129 | *.DotSettings.user 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # NuGet Symbol Packages 189 | *.snupkg 190 | # The packages folder can be ignored because of Package Restore 191 | **/[Pp]ackages/* 192 | # except build/, which is used as an MSBuild target. 193 | !**/[Pp]ackages/build/ 194 | # Uncomment if necessary however generally it will be regenerated when needed 195 | #!**/[Pp]ackages/repositories.config 196 | # NuGet v3's project.json files produces more ignorable files 197 | *.nuget.props 198 | *.nuget.targets 199 | 200 | # Microsoft Azure Build Output 201 | csx/ 202 | *.build.csdef 203 | 204 | # Microsoft Azure Emulator 205 | ecf/ 206 | rcf/ 207 | 208 | # Windows Store app package directories and files 209 | AppPackages/ 210 | BundleArtifacts/ 211 | Package.StoreAssociation.xml 212 | _pkginfo.txt 213 | *.appx 214 | *.appxbundle 215 | *.appxupload 216 | 217 | # Visual Studio cache files 218 | # files ending in .cache can be ignored 219 | *.[Cc]ache 220 | # but keep track of directories ending in .cache 221 | !?*.[Cc]ache/ 222 | 223 | # Others 224 | ClientBin/ 225 | ~$* 226 | *~ 227 | *.dbmdl 228 | *.dbproj.schemaview 229 | *.jfm 230 | *.pfx 231 | *.publishsettings 232 | orleans.codegen.cs 233 | 234 | # Including strong name files can present a security risk 235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 236 | #*.snk 237 | 238 | # Since there are multiple workflows, uncomment next line to ignore bower_components 239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 240 | #bower_components/ 241 | 242 | # RIA/Silverlight projects 243 | Generated_Code/ 244 | 245 | # Backup & report files from converting an old project file 246 | # to a newer Visual Studio version. Backup files are not needed, 247 | # because we have git ;-) 248 | _UpgradeReport_Files/ 249 | Backup*/ 250 | UpgradeLog*.XML 251 | UpgradeLog*.htm 252 | ServiceFabricBackup/ 253 | *.rptproj.bak 254 | 255 | # SQL Server files 256 | *.mdf 257 | *.ldf 258 | *.ndf 259 | 260 | # Business Intelligence projects 261 | *.rdl.data 262 | *.bim.layout 263 | *.bim_*.settings 264 | *.rptproj.rsuser 265 | *- [Bb]ackup.rdl 266 | *- [Bb]ackup ([0-9]).rdl 267 | *- [Bb]ackup ([0-9][0-9]).rdl 268 | 269 | # Microsoft Fakes 270 | FakesAssemblies/ 271 | 272 | # GhostDoc plugin setting file 273 | *.GhostDoc.xml 274 | 275 | # Node.js Tools for Visual Studio 276 | .ntvs_analysis.dat 277 | node_modules/ 278 | 279 | # Visual Studio 6 build log 280 | *.plg 281 | 282 | # Visual Studio 6 workspace options file 283 | *.opt 284 | 285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 286 | *.vbw 287 | 288 | # Visual Studio LightSwitch build output 289 | **/*.HTMLClient/GeneratedArtifacts 290 | **/*.DesktopClient/GeneratedArtifacts 291 | **/*.DesktopClient/ModelManifest.xml 292 | **/*.Server/GeneratedArtifacts 293 | **/*.Server/ModelManifest.xml 294 | _Pvt_Extensions 295 | 296 | # Paket dependency manager 297 | .paket/paket.exe 298 | paket-files/ 299 | 300 | # FAKE - F# Make 301 | .fake/ 302 | 303 | # CodeRush personal settings 304 | .cr/personal 305 | 306 | # Python Tools for Visual Studio (PTVS) 307 | __pycache__/ 308 | *.pyc 309 | 310 | # Cake - Uncomment if you are using it 311 | # tools/** 312 | # !tools/packages.config 313 | 314 | # Tabs Studio 315 | *.tss 316 | 317 | # Telerik's JustMock configuration file 318 | *.jmconfig 319 | 320 | # BizTalk build output 321 | *.btp.cs 322 | *.btm.cs 323 | *.odx.cs 324 | *.xsd.cs 325 | 326 | # OpenCover UI analysis results 327 | OpenCover/ 328 | 329 | # Azure Stream Analytics local run output 330 | ASALocalRun/ 331 | 332 | # MSBuild Binary and Structured Log 333 | *.binlog 334 | 335 | # NVidia Nsight GPU debugger configuration file 336 | *.nvuser 337 | 338 | # MFractors (Xamarin productivity tool) working folder 339 | .mfractor/ 340 | 341 | # Local History for Visual Studio 342 | .localhistory/ 343 | 344 | # BeatPulse healthcheck temp database 345 | healthchecksdb 346 | 347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 348 | MigrationBackup/ 349 | 350 | # Ionide (cross platform F# VS Code tools) working folder 351 | .ionide/ 352 | 353 | .DS_Store -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/bin/Debug/netcoreapp3.1/Xunit.Microsoft.DependencyInjection.ExampleTests.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/examples/Xunit.Microsoft.DependencyInjection.ExampleTests", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this library, please cite it as below." 3 | authors: 4 | - family-names: "Sabet" 5 | given-names: "Arash" 6 | orcid: "https://orcid.org/0009-0004-0416-9935" 7 | title: "Xunit Dependency Injection" 8 | version: 8.2.2 9 | # doi: 10.5281/zenodo.1234 10 | date-released: 2024-09-23 11 | url: "https://github.com/Umplify/xunit-dependency-injection" 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at info@umplify.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024, Umplify Technologies Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://dev.azure.com/umplify/Grain/_apis/build/status/Xunit/xunit-dependency-injection-PR?branchName=refs%2Fpull%2F94%2Fmerge)](https://dev.azure.com/umplify/Grain/_build/latest?definitionId=18&branchName=refs%2Fpull%2F94%2Fmerge) 2 | ![Nuget](https://img.shields.io/nuget/v/Xunit.Microsoft.DependencyInjection) 3 | ![Nuget](https://img.shields.io/nuget/dt/Xunit.Microsoft.DependencyInjection) 4 | 5 | # Xunit Dependency Injection framework - .NET 9.0 6 | 7 | Xunit does not support any built-in dependency injection features, therefore developers have to come up with a solution to recruit their favourite dependency injection framework in their tests. 8 | 9 | This library brings in Microsoft's dependency injection container to Xunit by leveraging Xunit's fixture. 10 | 11 | ## Getting started 12 | 13 | ### Nuget package 14 | 15 | First add the following [nuget package](https://www.nuget.org/packages/Xunit.Microsoft.DependencyInjection/) to your Xunit project: 16 | 17 | ```ps 18 | Install-Package Xunit.Microsoft.DependencyInjection 19 | ``` 20 | 21 | ### Setup your fixture 22 | 23 | The abstract class of `Xunit.Microsoft.DependencyInjection.Abstracts.TestBedFixture` contains the necessary functionalities to add services and configurations to Microsoft's dependency injection container. Your concrete test fixture class must derive from this abstract class and implement the following two abstract methods: 24 | 25 | ```csharp 26 | protected abstract void AddServices(IServiceCollection services, IConfiguration? configuration); 27 | protected abstract IEnumerable GetTestAppSettings(); 28 | protected abstract ValueTask DisposeAsyncCore(); 29 | ``` 30 | 31 | `GetConfigurationFiles(...)` method returns a collection of the configuration files in your Xunit test project to the framework. `AddServices(...)` method must be used to wire up the implemented services. 32 | 33 | #### Secret manager 34 | 35 | [Secret manage](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&tabs=windows#how-the-secret-manager-tool-works) is a great tool to store credentials, api keys and other secret information for development purpose. This library has started supporting user secrets from version 8.2.0 onwards. To utilize user secrets in your tests, simply override the `virtual` method below from `TestBedFixture` class: 36 | 37 | ```csharp 38 | protected override void AddUserSecrets(IConfigurationBuilder configurationBuilder); 39 | ``` 40 | 41 | ### Access the wired up services 42 | 43 | There are two method that you can use to access the wired up service depending on your context: 44 | 45 | ```csharp 46 | public T GetScopedService(ITestOutputHelper testOutputHelper); 47 | public T GetService(ITestOutputHelper testOutputHelper); 48 | ``` 49 | 50 | To access async scopes simply call the following method in the abstract fixture class: 51 | 52 | ```csharp 53 | public AsyncServiceScope GetAsyncScope(ITestOutputHelper testOutputHelper) 54 | ``` 55 | 56 | ### Accessing the keyed wired up services in .NET 9.0 57 | 58 | You can call the following method to access the keyed already-wired up services: 59 | 60 | ```csharp 61 | T? GetKeyedService([DisallowNull] string key, ITestOutputHelper testOutputHelper); 62 | ``` 63 | 64 | ### Adding custom logging provider 65 | 66 | Test developers can add their own desired logger provider by overriding ```AddLoggingProvider(...)``` virtual method defined in ```TestBedFixture``` class. 67 | 68 | ### Preparing Xunit test classes 69 | 70 | Your Xunit test class must be derived from ```Xunit.Microsoft.DependencyInjection.Abstracts.TestBed``` class where ```T``` should be your fixture class derived from ```TestBedFixture```. 71 | 72 | Also, the test class should be decorated by the following attribute: 73 | 74 | ```csharp 75 | [CollectionDefinition("Dependency Injection")] 76 | ``` 77 | 78 | #### Clearing managed resources 79 | 80 | To have managed resources cleaned up, simply override the virtual method of `Clear()`. This is an optional step. 81 | 82 | #### Clearing managed resourced asynchronously 83 | 84 | Simply override the virtual method of `DisposeAsyncCore()` for this purpose. This is also an optional step. 85 | 86 | ## Running tests in order 87 | 88 | The library also has a bonus feature that simplifies running tests in order. The test class does not have to be derived from ```TestBed``` class though and it can apply to all Xunit classes. 89 | 90 | Decorate your Xunit test class with the following attribute and associate ```TestOrder(...)``` with ```Fact``` and ```Theory```: 91 | 92 | ```csharp 93 | [TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")] 94 | ``` 95 | 96 | ## Supporting configuration from `UserSecrets` 97 | 98 | This library's `TestBedFixture` abstract class exposes an instance of `IConfigurationBuilder` that can be used to support `UserSecrets` when configuring the test projects: 99 | 100 | ```csharp 101 | public IConfigurationBuilder ConfigurationBuilder { get; private set; } 102 | ``` 103 | 104 | ## Examples 105 | 106 | * Please [follow this link](https://github.com/Umplify/xunit-dependency-injection/tree/main/examples/Xunit.Microsoft.DependencyInjection.ExampleTests) to view a couple of examples on utilizing this library. 107 | * [Digital Silo](https://digitalsilo.io/)'s unit tests and integration tests are using this library. 108 | 109 | ### One more thing 110 | 111 | Do not forget to include the following nuget packages to your Xunit project: 112 | 113 | * Microsoft.Extensions.DependencyInjection 114 | * Microsoft.Extensions.Configuration 115 | * Microsoft.Extensions.Options 116 | * Microsoft.Extensions.Configuration.Binder 117 | * Microsoft.Extensions.Configuration.FileExtensions 118 | * Microsoft.Extensions.Configuration.Json 119 | * Microsoft.Extensions.Logging 120 | * Microsoft.Extensions.Configuration.EnvironmentVariables 121 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /azure-pipeline-PR.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | Major: 9 3 | Minor: 0 4 | Patch: 0 5 | BuildConfiguration: Release 6 | 7 | name: $(Major).$(Minor).$(Patch).$(rev:r) 8 | trigger: 9 | - master 10 | 11 | pool: 12 | vmImage: 'ubuntu-latest' 13 | 14 | steps: 15 | - script: echo Build Number $(name) 16 | - task: UseDotNet@2 17 | displayName: 'Use .NET 9.0 sdk' 18 | inputs: 19 | packageType: sdk 20 | version: 9.0.300 21 | installationPath: $(Agent.ToolsDirectory)/dotnet 22 | - script: echo Started restoring the source code 23 | - task: DotNetCoreCLI@2 24 | displayName: 'Restoring' 25 | inputs: 26 | command: 'restore' 27 | projects: | 28 | **/Xunit.Microsoft.DependencyInjection.csproj 29 | **/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj 30 | feedsToUse: 'select' 31 | - script: echo Started building the source code 32 | - task: DotNetCoreCLI@2 33 | displayName: 'Building' 34 | inputs: 35 | command: 'build' 36 | projects: | 37 | **/Xunit.Microsoft.DependencyInjection.csproj 38 | **/examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj 39 | arguments: '--configuration $(BuildConfiguration)' 40 | 41 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | Major: 9 3 | Minor: 0 4 | Revision: 5 5 | BuildConfiguration: Release 6 | 7 | name: $(Major).$(Minor).$(Revision) 8 | pr: none 9 | trigger: 10 | batch: true 11 | tags: 12 | include: 13 | - refs/tags/* 14 | paths: 15 | exclude: 16 | - examples/* 17 | - README.md 18 | 19 | pool: 20 | vmImage: 'ubuntu-22.04' 21 | 22 | steps: 23 | - script: echo Build Number $(name) 24 | - task: UseDotNet@2 25 | displayName: 'Use .NET 9.0 sdk' 26 | inputs: 27 | packageType: sdk 28 | version: 9.0.300 29 | installationPath: $(Agent.ToolsDirectory)/dotnet 30 | - script: echo Started restoring the source code 31 | - task: DotNetCoreCLI@2 32 | displayName: 'Restoring' 33 | inputs: 34 | command: 'restore' 35 | projects: '**/Xunit.Microsoft.DependencyInjection.csproj' 36 | feedsToUse: 'select' 37 | - script: echo Started building the source code 38 | - task: DotNetCoreCLI@2 39 | displayName: 'Building' 40 | inputs: 41 | command: 'build' 42 | projects: '**/Xunit.Microsoft.DependencyInjection.csproj' 43 | arguments: '--configuration $(BuildConfiguration)' 44 | - task: DotNetCoreCLI@2 45 | displayName: 'Running tests in example folder' 46 | continueOnError: true 47 | inputs: 48 | command: 'test' 49 | projects: '**/examples/Xunit.Microsoft.DependencyInjection.ExampleTests' 50 | arguments: '--configuration $(BuildConfiguration)' 51 | testRunTitle: 'Running sample tests' 52 | 53 | - script: echo Started publishing 54 | - task: DotNetCoreCLI@2 55 | displayName: 'Publishing' 56 | inputs: 57 | command: 'publish' 58 | publishWebProjects: false 59 | projects: '**/Xunit.Microsoft.DependencyInjection.csproj' 60 | arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)' 61 | - script: echo Started dropping 62 | - task: PublishBuildArtifacts@1 63 | displayName: 'Dropping' 64 | inputs: 65 | PathtoPublish: '$(Build.ArtifactStagingDirectory)' 66 | ArtifactName: 'drop' 67 | publishLocation: 'Container' 68 | 69 | - script: echo Started packing and pushing 70 | 71 | - task: NuGetCommand@2 72 | displayName: 'Packing' 73 | inputs: 74 | command: 'pack' 75 | packagesToPack: '**/Xunit.Microsoft.DependencyInjection.csproj' 76 | versioningScheme: 'byEnvVar' 77 | versionEnvVar: 'Build.BuildNumber' 78 | - task: NuGetCommand@2 79 | displayName: 'Pushing to nuget.org' 80 | inputs: 81 | command: 'push' 82 | packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg' 83 | nuGetFeedType: 'external' 84 | publishFeedCredentials: 'NuGet' 85 | allowPackageConflicts: true 86 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/CalculatorTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using Options = Xunit.Microsoft.DependencyInjection.ExampleTests.Services.Options; 3 | 4 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests; 5 | 6 | public class CalculatorTests : TestBed 7 | { 8 | private readonly Options _options; 9 | 10 | public CalculatorTests(ITestOutputHelper testOutputHelper, TestProjectFixture fixture) 11 | : base(testOutputHelper, fixture) => _options = _fixture.GetService>(_testOutputHelper)!.Value; 12 | 13 | [Theory] 14 | [InlineData(1, 2)] 15 | public async Task TestServiceAsync(int x, int y) 16 | { 17 | var calculator = _fixture.GetService(_testOutputHelper)!; 18 | var calculatedValue = await calculator.AddAsync(x, y); 19 | var expected = _options.Rate * (x + y); 20 | Assert.True(expected == calculatedValue); 21 | } 22 | 23 | [Theory] 24 | [InlineData(1, 2)] 25 | public async Task TestScopedServiceAsync(int x, int y) 26 | { 27 | var calculator = _fixture.GetScopedService(_testOutputHelper)!; 28 | var calculatedValue = await calculator.AddAsync(x, y); 29 | var expected = _options.Rate * (x + y); 30 | Assert.True(expected == calculatedValue); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Fixtures/TestProjectFixture.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Fixtures; 2 | 3 | public class TestProjectFixture : TestBedFixture 4 | { 5 | protected override void AddServices(IServiceCollection services, IConfiguration? configuration) 6 | => services 7 | .AddTransient() 8 | .AddKeyedTransient("Porsche") 9 | .AddKeyedTransient("Toyota") 10 | .Configure(config => configuration?.GetSection("Options").Bind(config)) 11 | .Configure(config => configuration?.GetSection(nameof(SecretValues)).Bind(config)); 12 | 13 | protected override ValueTask DisposeAsyncCore() 14 | => new(); 15 | 16 | protected override IEnumerable GetTestAppSettings() 17 | { 18 | yield return new() { Filename = "appsettings.json", IsOptional = false }; 19 | } 20 | 21 | protected override void AddUserSecrets(IConfigurationBuilder configurationBuilder) 22 | => configurationBuilder.AddUserSecrets(); 23 | } 24 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.Extensions.Configuration; 2 | global using Microsoft.Extensions.DependencyInjection; 3 | global using System.Collections.Generic; 4 | global using System.Threading.Tasks; 5 | global using Xunit.Abstractions; 6 | global using Xunit.Microsoft.DependencyInjection.Abstracts; 7 | global using Xunit.Microsoft.DependencyInjection.Attributes; 8 | global using Xunit.Microsoft.DependencyInjection.ExampleTests.Fixtures; 9 | global using Xunit.Microsoft.DependencyInjection.ExampleTests.Services; -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/KeyedServicesTests.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests; 2 | 3 | public class KeyedServicesTests(ITestOutputHelper testOutputHelper, TestProjectFixture fixture) : TestBed(testOutputHelper, fixture) 4 | { 5 | [Theory] 6 | [InlineData(nameof(Porsche))] 7 | [InlineData(nameof(Toyota))] 8 | public void GetKeyedService(string key) 9 | { 10 | var carMaker = _fixture.GetKeyedService(key, _testOutputHelper)!; 11 | Assert.Equal(key, carMaker.Manufacturer); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/SecretValues.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests; 2 | 3 | public record SecretValues 4 | { 5 | public string? Secret1 { get; set; } 6 | public string? Secret2 { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/Calculator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 5 | 6 | public class Calculator(ILogger logger, IOptions option) : ICalculator 7 | { 8 | private readonly Options _option = option.Value; 9 | private readonly ILogger _logger = logger; 10 | 11 | public Task AddAsync(int x, int y) 12 | { 13 | var result = (x + y) * _option.Rate; 14 | _logger.LogInformation("The result is {@Result}", result); 15 | return Task.FromResult(result); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/ICalculator.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 2 | 3 | public interface ICalculator 4 | { 5 | Task AddAsync(int x, int y); 6 | } 7 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/ICarMaker.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 2 | 3 | internal interface ICarMaker 4 | { 5 | string Manufacturer { get; } 6 | } 7 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/Options.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 2 | 3 | public class Options 4 | { 5 | public int Rate { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/Porsche.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 2 | 3 | internal class Porsche : ICarMaker 4 | { 5 | public string Manufacturer =>nameof(Porsche); 6 | } -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Services/Toyota.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests.Services; 2 | 3 | internal class Toyota : ICarMaker 4 | { 5 | public string Manufacturer => nameof(Toyota); 6 | } 7 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/UnitTests.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests; 2 | 3 | [TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")] 4 | public class UnitTests 5 | { 6 | [Fact, TestOrder(1)] 7 | public void Test1() 8 | => Assert.Equal(1, 1); 9 | 10 | [Fact, TestOrder(2)] 11 | public void Test2() 12 | => Assert.NotEqual(1, 0); 13 | 14 | [Fact, TestOrder(3)] 15 | public async Task Test3() 16 | { 17 | await Task.Delay(3000); 18 | Assert.Equal(1, 1); 19 | } 20 | 21 | [Fact, TestOrder(4)] 22 | public async Task Test4() 23 | { 24 | await Task.Delay(5000); 25 | Assert.True(1 > 0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/UserSecretTests.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Xunit.Microsoft.DependencyInjection.ExampleTests; 5 | 6 | public class UserSecretTests(ITestOutputHelper testOutputHelper, TestProjectFixture fixture) : TestBed(testOutputHelper, fixture) 7 | { 8 | [Fact] 9 | public void TestSecretValues() 10 | { 11 | /* 12 | * TODO: Create a user secret entry like the following payload in user secrets and remove the same from appsettings.json file: 13 | * 14 | * "SecretValues": { 15 | * "Secret1": "secret1value", 16 | * "Secret2": "secret2value" 17 | * } 18 | */ 19 | var secretValues = _fixture.GetService>(_testOutputHelper)!.Value; 20 | Assert.NotEmpty(secretValues?.Secret1 ?? string.Empty); 21 | Assert.NotEmpty(secretValues?.Secret1 ?? string.Empty); 22 | } 23 | } -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/Xunit.Microsoft.DependencyInjection.ExampleTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | false 7 | enable 8 | enable 9 | 59bdc82c-5628-47c8-a5ec-3630c3a2bc45 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | PreserveNewest 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/Xunit.Microsoft.DependencyInjection.ExampleTests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Options": { 3 | "Rate": 10 4 | }, 5 | "SecretValues": { 6 | "Secret1": "StoreSecret1InUserSecrets", 7 | "Secret2": "StoreSecret2InUserSecrets" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /google1399ddbab0b8eed5.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google1399ddbab0b8eed5.html -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = crlf 5 | 6 | [*.sql] 7 | indent_style = tab 8 | 9 | [*.cs] 10 | indent_style = tab 11 | trim_trailing_whitespace = true 12 | 13 | dotnet_sort_system_directives_first = false 14 | 15 | dotnet_style_qualification_for_field = false:suggestion 16 | dotnet_style_qualification_for_method = false:suggestion 17 | dotnet_style_qualification_for_event = false:suggestion 18 | dotnet_style_qualification_for_property = false:suggestion 19 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 20 | dotnet_style_predefined_type_for_member_access = true:warning 21 | dotnet_style_object_initializer = true:suggestion 22 | dotnet_style_collection_initializer = true:suggestion 23 | dotnet_style_coalesce_expression = true:suggestion 24 | dotnet_style_require_accessibility_modifiers = always:suggestion 25 | dotnet_style_readonly_field = true:warning 26 | dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none 27 | dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none 28 | dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none 29 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:none 30 | dotnet_style_object_initializer = true:warning 31 | dotnet_style_collection_initializer = true:suggestion 32 | dotnet_style_explicit_tuple_names = true:suggestion 33 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 34 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 35 | dotnet_style_prefer_auto_properties = true:suggestion 36 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 37 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 38 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 39 | dotnet_style_coalesce_expression = true:warning 40 | dotnet_style_null_propagation = true:suggestion 41 | 42 | csharp_style_var_for_built_in_types = true:suggestion 43 | csharp_style_var_when_type_is_apparent = true:suggestion 44 | csharp_style_var_elsewhere = true:suggestion 45 | csharp_style_conditional_delegate_call = true:suggestion 46 | csharp_style_var_for_built_in_types = true:suggestion 47 | csharp_style_var_elsewhere = true:suggestion 48 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 49 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 50 | csharp_style_inlined_variable_declaration = true:suggestion 51 | csharp_style_throw_expression = true:warning 52 | csharp_style_expression_bodied_methods = true:warning 53 | csharp_style_expression_bodied_constructors = true:warning 54 | csharp_style_expression_bodied_operators = true:warning 55 | csharp_style_expression_bodied_properties = true:warning 56 | csharp_style_expression_bodied_indexers = true:warning 57 | csharp_style_expression_bodied_accessors = true:warning 58 | csharp_style_deconstructed_variable_declaration = true:suggestion 59 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 60 | csharp_style_namespace_declaration = file_scoped:warning 61 | 62 | csharp_prefer_simple_default_expression = true:suggestion 63 | csharp_prefer_braces = true:warning 64 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 65 | csharp_indent_case_contents = true 66 | csharp_indent_switch_labels = true 67 | csharp_indent_labels = one_less 68 | 69 | csharp_preserve_single_line_statements = false 70 | csharp_preserve_single_line_blocks = true 71 | 72 | csharp_space_after_cast = false 73 | csharp_space_after_keywords_in_control_flow_statements = true 74 | csharp_space_between_method_declaration_parameter_list_parentheses = false 75 | csharp_space_between_method_call_parameter_list_parentheses = false 76 | csharp_space_between_parentheses = false 77 | 78 | csharp_new_line_before_open_brace = all 79 | csharp_new_line_before_else = true 80 | csharp_new_line_before_catch = true 81 | csharp_new_line_before_finally = true 82 | csharp_new_line_before_members_in_object_initializers = true 83 | csharp_new_line_before_members_in_anonymous_types = true 84 | csharp_new_line_within_query_expression_clauses = true 85 | 86 | dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods 87 | dotnet_naming_rule.async_methods_end_in_async.style = end_in_async 88 | dotnet_naming_rule.async_methods_end_in_async.severity = warning 89 | dotnet_naming_symbols.any_async_methods.applicable_kinds = method 90 | dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * 91 | dotnet_naming_symbols.any_async_methods.required_modifiers = async 92 | dotnet_naming_style.end_in_async.required_suffix = Async 93 | dotnet_naming_style.end_in_async.capitalization = pascal_case -------------------------------------------------------------------------------- /src/Abstracts/TestBed.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Abstracts; 2 | 3 | public class TestBed(ITestOutputHelper testOutputHelper, TFixture fixture) : IDisposable, IClassFixture, IAsyncDisposable 4 | where TFixture : class 5 | { 6 | protected readonly ITestOutputHelper _testOutputHelper = testOutputHelper; 7 | protected readonly TFixture _fixture = fixture; 8 | private bool _disposedValue; 9 | private bool _disposedAsync; 10 | 11 | protected virtual void Dispose(bool disposing) 12 | { 13 | if (!_disposedValue) 14 | { 15 | if (disposing) 16 | { 17 | // TODO: dispose managed state (managed objects) 18 | Clear(); 19 | } 20 | 21 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 22 | // TODO: set large fields to null 23 | _disposedValue = true; 24 | } 25 | } 26 | 27 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 28 | // ~AbstractTest() 29 | // { 30 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 31 | // Dispose(disposing: false); 32 | // } 33 | 34 | public void Dispose() 35 | { 36 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 37 | Dispose(disposing: true); 38 | GC.SuppressFinalize(this); 39 | } 40 | 41 | public async ValueTask DisposeAsync() 42 | { 43 | if (!_disposedAsync) 44 | { 45 | await DisposeAsyncCore(); 46 | GC.SuppressFinalize(this); 47 | _disposedAsync = true; 48 | } 49 | } 50 | 51 | protected virtual void Clear() { } 52 | protected virtual ValueTask DisposeAsyncCore() => new(); 53 | } 54 | -------------------------------------------------------------------------------- /src/Abstracts/TestBedFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Xunit.Microsoft.DependencyInjection.Abstracts; 4 | 5 | public abstract class TestBedFixture : IDisposable, IAsyncDisposable 6 | { 7 | private readonly ServiceCollection _services; 8 | private ServiceProvider? _serviceProvider; 9 | private bool _disposedValue; 10 | private bool _disposedAsync; 11 | private bool _servicesAdded; 12 | 13 | protected TestBedFixture() 14 | { 15 | _services = new ServiceCollection(); 16 | ConfigurationBuilder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()); 17 | AddUserSecrets(ConfigurationBuilder); 18 | Configuration = GetConfigurationRoot(); 19 | _servicesAdded = false; 20 | } 21 | 22 | public IConfigurationRoot? Configuration { get; private set; } 23 | public IConfigurationBuilder ConfigurationBuilder { get; private set; } 24 | 25 | public ServiceProvider GetServiceProvider(ITestOutputHelper testOutputHelper) 26 | { 27 | if (_serviceProvider is not null) 28 | { 29 | return _serviceProvider; 30 | } 31 | if(!_servicesAdded) 32 | { 33 | AddServices(_services, Configuration); 34 | _services.AddLogging(loggingBuilder => AddLoggingProvider(loggingBuilder, new OutputLoggerProvider(testOutputHelper))); 35 | _services.AddOptions(); 36 | _servicesAdded = true; 37 | } 38 | return _serviceProvider = _services.BuildServiceProvider(); 39 | } 40 | 41 | public T? GetScopedService(ITestOutputHelper testOutputHelper) 42 | { 43 | var serviceProvider = GetServiceProvider(testOutputHelper); 44 | using var scope = serviceProvider.CreateScope(); 45 | return scope.ServiceProvider.GetService(); 46 | } 47 | 48 | public AsyncServiceScope GetAsyncScope(ITestOutputHelper testOutputHelper) 49 | { 50 | var serviceProvider = GetServiceProvider(testOutputHelper); 51 | return serviceProvider.CreateAsyncScope(); 52 | } 53 | 54 | public T? GetService(ITestOutputHelper testOutputHelper) 55 | => GetServiceProvider(testOutputHelper).GetService(); 56 | 57 | public T? GetKeyedService([DisallowNull] string key, ITestOutputHelper testOutputHelper) 58 | => GetServiceProvider(testOutputHelper).GetKeyedService(key); 59 | 60 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 61 | // ~AbstractDependencyInjectionFixture() 62 | // { 63 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 64 | // Dispose(disposing: false); 65 | // } 66 | 67 | public void Dispose() 68 | { 69 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 70 | Dispose(disposing: true); 71 | GC.SuppressFinalize(this); 72 | } 73 | 74 | public async ValueTask DisposeAsync() 75 | { 76 | if (!_disposedAsync) 77 | { 78 | await DisposeAsyncCore(); 79 | Dispose(); 80 | _disposedAsync = true; 81 | } 82 | GC.SuppressFinalize(this); 83 | } 84 | 85 | protected abstract void AddServices(IServiceCollection services, IConfiguration? configuration); 86 | protected abstract IEnumerable GetTestAppSettings(); 87 | protected abstract ValueTask DisposeAsyncCore(); 88 | 89 | protected virtual ILoggingBuilder AddLoggingProvider(ILoggingBuilder loggingBuilder, ILoggerProvider loggerProvider) 90 | => loggingBuilder.AddProvider(loggerProvider); 91 | 92 | protected virtual void AddUserSecrets(IConfigurationBuilder configurationBuilder) { } 93 | 94 | private IConfigurationRoot? GetConfigurationRoot() 95 | { 96 | var testAppSettings = GetTestAppSettings(); 97 | return 98 | testAppSettings.All(setting => !string.IsNullOrEmpty(setting.Filename)) 99 | ? GetConfigurationRoot(testAppSettings) 100 | : default; 101 | } 102 | 103 | private IConfigurationRoot GetConfigurationRoot(IEnumerable configurationFiles) 104 | { 105 | foreach (var configurationFile in configurationFiles) 106 | { 107 | ConfigurationBuilder.AddJsonFile(configurationFile.Filename!, optional: configurationFile.IsOptional); 108 | } 109 | ConfigurationBuilder.AddEnvironmentVariables(); 110 | return ConfigurationBuilder.Build(); 111 | } 112 | 113 | protected virtual void Dispose(bool disposing) 114 | { 115 | if (!_disposedValue) 116 | { 117 | if (disposing) 118 | { 119 | // TODO: dispose managed state (managed objects) 120 | if (_serviceProvider is not null) 121 | { 122 | ((ServiceProvider)_serviceProvider).Dispose(); 123 | } 124 | _services.Clear(); 125 | } 126 | 127 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 128 | // TODO: set large fields to null 129 | _disposedValue = true; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Attributes/TestOrderAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Attributes; 2 | 3 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 4 | public class TestOrderAttribute(int priority) : Attribute 5 | { 6 | public int Priority { get; } = priority; 7 | } 8 | -------------------------------------------------------------------------------- /src/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.Extensions.Configuration; 2 | global using Microsoft.Extensions.DependencyInjection; 3 | global using Microsoft.Extensions.Logging; 4 | global using Xunit.Abstractions; 5 | global using Xunit.Microsoft.DependencyInjection.Attributes; 6 | global using Xunit.Microsoft.DependencyInjection.Logging; 7 | global using Xunit.Sdk; -------------------------------------------------------------------------------- /src/Logging/NilLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Logging; 2 | 3 | public sealed class NilLoggerProvider : ILoggerProvider 4 | { 5 | public ILogger CreateLogger(string categoryName) 6 | => new NilLogger(); 7 | 8 | public void Dispose() 9 | { 10 | } 11 | 12 | private class NilLogger : ILogger 13 | { 14 | public IDisposable? BeginScope(TState state) where TState : notnull 15 | => new NoOpDisposable(); 16 | 17 | public bool IsEnabled(LogLevel logLevel) 18 | => false; 19 | 20 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Logging/NoOpDisposable.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Logging; 2 | 3 | internal class NoOpDisposable : IDisposable 4 | { 5 | public void Dispose() { } 6 | } 7 | -------------------------------------------------------------------------------- /src/Logging/OutputLogger.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Logging; 2 | 3 | public class OutputLogger(string categoryName, ITestOutputHelper testOutputHelper) : ILogger 4 | { 5 | private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; 6 | private readonly string _categoryName = categoryName; 7 | 8 | public OutputLogger(ITestOutputHelper testOutputHelper) 9 | : this("Tests", testOutputHelper) 10 | { 11 | } 12 | 13 | public IDisposable? BeginScope(TState state) where TState : notnull 14 | => new NoOpDisposable(); 15 | 16 | public bool IsEnabled(LogLevel logLevel) 17 | => true; 18 | 19 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) 20 | { 21 | try 22 | { 23 | if (exception is not null) 24 | { 25 | _testOutputHelper.WriteLine($"{logLevel} - Category: {_categoryName} : {formatter(state, exception)} :: {DateTime.Now}"); 26 | } 27 | else 28 | { 29 | _testOutputHelper.WriteLine($"{logLevel} - Category: {_categoryName} : {state} :: {DateTime.Now}"); 30 | } 31 | } 32 | catch 33 | { 34 | //Ignore 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Logging/OutputLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.Logging; 2 | 3 | public class OutputLoggerProvider(ITestOutputHelper testOutputHelper) : ILoggerProvider 4 | { 5 | private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; 6 | 7 | public ILogger CreateLogger(string categoryName) 8 | => new OutputLogger(categoryName, _testOutputHelper); 9 | 10 | public void Dispose() => GC.SuppressFinalize(this); 11 | } 12 | -------------------------------------------------------------------------------- /src/TestAppSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection; 2 | 3 | public class TestAppSettings 4 | { 5 | public string? Filename { get; set; } 6 | public bool IsOptional { get; set; } = false; 7 | } 8 | -------------------------------------------------------------------------------- /src/TestsOrder/TestPriorityOrderer.cs: -------------------------------------------------------------------------------- 1 | namespace Xunit.Microsoft.DependencyInjection.TestsOrder; 2 | 3 | public class TestPriorityOrderer : ITestCaseOrderer 4 | { 5 | public IEnumerable OrderTestCases(IEnumerable testCases) 6 | where TTestCase : ITestCase 7 | { 8 | var sortedMethods = new SortedDictionary>(); 9 | 10 | foreach (var testCase in testCases) 11 | { 12 | var priority = 0; 13 | 14 | foreach (var attr in testCase.TestMethod.Method.GetCustomAttributes(typeof(TestOrderAttribute).AssemblyQualifiedName)) 15 | { 16 | priority = attr.GetNamedArgument("Priority"); 17 | } 18 | 19 | GetOrCreate(sortedMethods, priority).Add(testCase); 20 | } 21 | 22 | foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) 23 | { 24 | list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); 25 | foreach (var testCase in list) 26 | { 27 | yield return testCase; 28 | } 29 | } 30 | } 31 | 32 | private TValue GetOrCreate(IDictionary dictionary, TKey key) 33 | where TValue : new() 34 | { 35 | if (dictionary.TryGetValue(key, out var result)) 36 | { 37 | return result; 38 | } 39 | 40 | result = new TValue(); 41 | dictionary[key] = result; 42 | 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Xunit.Microsoft.DependencyInjection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | latest 5 | enable 6 | enable 7 | README.md 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Xunit.Microsoft.DependencyInjection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34330.188 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xunit.Microsoft.DependencyInjection", "Xunit.Microsoft.DependencyInjection.csproj", "{A2F3411E-6B6E-4B7B-BCC1-4F4BA1B345E7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xunit.Microsoft.DependencyInjection.ExampleTests", "..\examples\Xunit.Microsoft.DependencyInjection.ExampleTests\Xunit.Microsoft.DependencyInjection.ExampleTests.csproj", "{B8F50313-1C91-4212-A11A-6D3589BA8DF7}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A2F3411E-6B6E-4B7B-BCC1-4F4BA1B345E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {A2F3411E-6B6E-4B7B-BCC1-4F4BA1B345E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {A2F3411E-6B6E-4B7B-BCC1-4F4BA1B345E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {A2F3411E-6B6E-4B7B-BCC1-4F4BA1B345E7}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {B8F50313-1C91-4212-A11A-6D3589BA8DF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {B8F50313-1C91-4212-A11A-6D3589BA8DF7}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {B8F50313-1C91-4212-A11A-6D3589BA8DF7}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {B8F50313-1C91-4212-A11A-6D3589BA8DF7}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D7DF7E75-1360-4175-A3E2-BEA2D338EB23} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/package.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Xunit.Microsoft.DependencyInjection 5 | Microsoft Dependency Injection utility for Xunit 6 | 9.0 7 | Arash Sabet 8 | Umplify Technologies Inc. 9 | https://github.com/Umplify/xunit-dependency-injection/blob/main/LICENSE 10 | https://umplify.github.io/xunit-dependency-injection/ 11 | http://umplify.com/wp-content/uploads/2017/11/umplify_logo.png 12 | true 13 | This package contains the necessary classes and features to leverage Xunit's fixture class for Microsoft dependency inection framework. 14 | 2025 - All rights reserved, Umplify Technologies Inc. 15 | xunit dependency-injection microsoft-dependency-injection xunit-dependency-injection 16 | 17 | 18 | --------------------------------------------------------------------------------