├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── build.yml ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── .prettierrc ├── .vscode └── settings.json ├── .whitesource ├── CONTRIBUTING.md ├── FluentAssertions.Autofac.Tests ├── AutofacAssertionExtensions_Should.cs ├── AutofacExtensions_Should.cs ├── BuilderAssertions_Should.cs ├── ContainerAssertions_Should.cs ├── ContainerRegistrationAssertions_Should.cs ├── FluentAssertions.Autofac.Tests.csproj ├── Module_Should.cs ├── RegisterAssertions_Should.cs ├── RegisterGenericSourceAssertions_Should.cs ├── RegistrationAssertions_Should.cs ├── ResolveAssertions_Should.cs ├── TestExtensions_Should.cs ├── TestHelper.cs └── TypeScanningAssertions_Should.cs ├── FluentAssertions.Autofac.sln ├── FluentAssertions.Autofac.sln.DotSettings ├── FluentAssertions.Autofac.v3.ncrunchsolution ├── FluentAssertions.Autofac ├── AutofacAssertionExtensions.cs ├── AutofacExtensions.cs ├── BuilderAssertions.cs ├── ContainerAssertions.cs ├── ContainerRegistrationAssertions.cs ├── FluentAssertions.Autofac.csproj ├── Module.cs ├── Package.nuspec ├── RegisterAssertions.cs ├── RegisterGenericSourceAssertions.cs ├── RegistrationAssertions.cs ├── ResolveAssertions.cs ├── TestExtensions.cs ├── TypeScanningAssertions.cs └── TypeScanningAssertionsExtensions.cs ├── GitVersion.yml ├── LICENSE ├── README.md ├── SampleLib ├── DeviceState.cs ├── IDeviceState.cs ├── INamedInstance.cs ├── ISampleInstance.cs ├── ISampleService.cs ├── NamedInstance.cs ├── OnlineState.cs ├── SampleLib.csproj ├── SampleModule.cs ├── SampleModule_Should.cs ├── SampleService.cs └── SampleStarter.cs ├── SolutionInfo.cs ├── _docs ├── index.md ├── usage.md └── why.md ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── .editorconfig ├── Build.cs ├── _build.csproj └── _build.csproj.DotSettings ├── global.json ├── key.public ├── key.snk └── solution.targets /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{xml,config,settings,yml,yaml,json,nuspec}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # CSharp and Visual Basic code style settings (Visual Studio default): 16 | # cf.: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 17 | [*.{cs,vb}] 18 | dotnet_style_qualification_for_field = false:suggestion 19 | dotnet_style_qualification_for_property = false:suggestion 20 | dotnet_style_qualification_for_method = false:suggestion 21 | dotnet_style_qualification_for_event = false:suggestion 22 | 23 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 24 | dotnet_style_predefined_type_for_member_access = true:suggestion 25 | 26 | dotnet_style_object_initializer = true:suggestion 27 | dotnet_style_collection_initializer = true:suggestion 28 | 29 | dotnet_style_explicit_tuple_names = true:suggestion 30 | 31 | dotnet_style_coalesce_expression = true:suggestion 32 | dotnet_style_null_propagation = true:suggestion 33 | 34 | csharp_style_var_for_built_in_types = true:suggestion 35 | csharp_style_var_when_type_is_apparent = true:suggestion 36 | csharp_style_var_elsewhere = true:suggestion 37 | 38 | csharp_style_expression_bodied_methods = false:none 39 | csharp_style_expression_bodied_constructors = false:none 40 | csharp_style_expression_bodied_operators = false:none 41 | 42 | csharp_style_expression_bodied_properties = true:none 43 | csharp_style_expression_bodied_indexers = false:none 44 | csharp_style_expression_bodied_accessors = false:none 45 | 46 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 47 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 48 | 49 | csharp_style_inlined_variable_declaration = true:suggestion 50 | 51 | csharp_prefer_simple_default_expression = true:suggestion 52 | 53 | csharp_style_throw_expression = true:suggestion 54 | csharp_style_conditional_delegate_call = false:suggestion 55 | 56 | csharp_prefer_braces = true:none 57 | 58 | dotnet_sort_system_directives_first = true 59 | 60 | csharp_new_line_before_open_brace = all 61 | csharp_new_line_before_else = true 62 | csharp_new_line_before_catch = true 63 | csharp_new_line_before_finally = true 64 | csharp_new_line_before_members_in_object_initializers = true 65 | csharp_new_line_before_members_in_anonymous_types = true 66 | csharp_new_line_between_query_expression_clauses = true 67 | 68 | csharp_indent_case_contents = true 69 | csharp_indent_switch_labels = true 70 | csharp_indent_labels = no_change 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_preserve_single_line_statements = true 79 | csharp_preserve_single_line_blocks = true 80 | 81 | dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods 82 | dotnet_naming_rule.async_methods_end_in_async.style = end_in_async 83 | dotnet_naming_rule.async_methods_end_in_async.severity = suggestion 84 | 85 | dotnet_naming_symbols.any_async_methods.applicable_kinds = method 86 | dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * 87 | dotnet_naming_symbols.any_async_methods.required_modifiers = async 88 | 89 | dotnet_naming_style.end_in_async.required_suffix = Async 90 | dotnet_naming_style.end_in_async.capitalization = pascal_case 91 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 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 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.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/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | tags: ['*'] 7 | paths-ignore: 8 | - _docs 9 | - "**/*.md" 10 | pull_request: 11 | branches: [main] 12 | 13 | jobs: 14 | build: 15 | runs-on: windows-2022 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: '0' # Use all history for gitversion, cf.: https://github.com/actions/checkout/issues/113 21 | 22 | - uses: actions/setup-java@v2 # Setup Java 11 for Sonar 23 | with: 24 | distribution: 'temurin' # https://github.com/actions/setup-java#supported-distributions 25 | java-version: '11' # https://docs.sonarqube.org/latest/requirements/requirements/ 26 | 27 | # Setup all needed dotnet sdks, cf.: 28 | # - https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e 29 | # - https://github.com/actions/setup-dotnet 30 | - name: Setup dotnet 31 | uses: actions/setup-dotnet@v1 32 | with: 33 | dotnet-version: | 34 | 3.1.x 35 | 5.0.x 36 | 6.0.x 37 | 38 | - run: .\build.ps1 CiBuild 39 | env: 40 | SONAR_LOGIN: '${{ secrets.SONAR_LOGIN }}' 41 | NUGET_API_KEY: '${{ secrets.NUGET_API_KEY }}' 42 | # Use Java 11 for Sonar Scanner, Pre-installed on windows-latest 43 | # cf.: https://github.com/actions/virtual-environments/blob/main/images/win/Windows2022-Readme.md#java 44 | #JAVA_HOME: "${{ env.JAVA_HOME_11_X64 }}" 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore coverage reports 2 | ./coverage/* 3 | 4 | # Ignore MSBuild sonarqube scanner work 5 | .sonarqube/ 6 | 7 | 8 | # Created by https://www.toptal.com/developers/gitignore/api/sonarqube,intellij+all,visualstudio,visualstudiocode 9 | # Edit at https://www.toptal.com/developers/gitignore?templates=sonarqube,intellij+all,visualstudio,visualstudiocode 10 | 11 | ### Intellij+all ### 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 13 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 14 | 15 | # User-specific stuff 16 | .idea/**/workspace.xml 17 | .idea/**/tasks.xml 18 | .idea/**/usage.statistics.xml 19 | .idea/**/dictionaries 20 | .idea/**/shelf 21 | 22 | # AWS User-specific 23 | .idea/**/aws.xml 24 | 25 | # Generated files 26 | .idea/**/contentModel.xml 27 | 28 | # Sensitive or high-churn files 29 | .idea/**/dataSources/ 30 | .idea/**/dataSources.ids 31 | .idea/**/dataSources.local.xml 32 | .idea/**/sqlDataSources.xml 33 | .idea/**/dynamic.xml 34 | .idea/**/uiDesigner.xml 35 | .idea/**/dbnavigator.xml 36 | 37 | # Gradle 38 | .idea/**/gradle.xml 39 | .idea/**/libraries 40 | 41 | # Gradle and Maven with auto-import 42 | # When using Gradle or Maven with auto-import, you should exclude module files, 43 | # since they will be recreated, and may cause churn. Uncomment if using 44 | # auto-import. 45 | # .idea/artifacts 46 | # .idea/compiler.xml 47 | # .idea/jarRepositories.xml 48 | # .idea/modules.xml 49 | # .idea/*.iml 50 | # .idea/modules 51 | # *.iml 52 | # *.ipr 53 | 54 | # CMake 55 | cmake-build-*/ 56 | 57 | # Mongo Explorer plugin 58 | .idea/**/mongoSettings.xml 59 | 60 | # File-based project format 61 | *.iws 62 | 63 | # IntelliJ 64 | out/ 65 | 66 | # mpeltonen/sbt-idea plugin 67 | .idea_modules/ 68 | 69 | # JIRA plugin 70 | atlassian-ide-plugin.xml 71 | 72 | # Cursive Clojure plugin 73 | .idea/replstate.xml 74 | 75 | # Crashlytics plugin (for Android Studio and IntelliJ) 76 | com_crashlytics_export_strings.xml 77 | crashlytics.properties 78 | crashlytics-build.properties 79 | fabric.properties 80 | 81 | # Editor-based Rest Client 82 | .idea/httpRequests 83 | 84 | # Android studio 3.1+ serialized cache file 85 | .idea/caches/build_file_checksums.ser 86 | 87 | ### Intellij+all Patch ### 88 | # Ignores the whole .idea folder and all .iml files 89 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 90 | 91 | .idea/ 92 | 93 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 94 | 95 | *.iml 96 | modules.xml 97 | .idea/misc.xml 98 | *.ipr 99 | 100 | # Sonarlint plugin 101 | .idea/sonarlint 102 | 103 | ### SonarQube ### 104 | # SonarQube ignore files. 105 | # 106 | # https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner 107 | # Sonar Scanner working directories 108 | .sonar/ 109 | .sonarqube/ 110 | .scannerwork/ 111 | 112 | # http://www.sonarlint.org/commandline/ 113 | # SonarLint working directories, configuration files (including credentials) 114 | .sonarlint/ 115 | 116 | ### VisualStudioCode ### 117 | .vscode/* 118 | !.vscode/settings.json 119 | !.vscode/tasks.json 120 | !.vscode/launch.json 121 | !.vscode/extensions.json 122 | *.code-workspace 123 | 124 | # Local History for Visual Studio Code 125 | .history/ 126 | 127 | ### VisualStudioCode Patch ### 128 | # Ignore all local history of files 129 | .history 130 | .ionide 131 | 132 | ### VisualStudio ### 133 | ## Ignore Visual Studio temporary files, build results, and 134 | ## files generated by popular Visual Studio add-ons. 135 | ## 136 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 137 | 138 | # User-specific files 139 | *.rsuser 140 | *.suo 141 | *.user 142 | *.userosscache 143 | *.sln.docstates 144 | 145 | # User-specific files (MonoDevelop/Xamarin Studio) 146 | *.userprefs 147 | 148 | # Mono auto generated files 149 | mono_crash.* 150 | 151 | # Build results 152 | [Dd]ebug/ 153 | [Dd]ebugPublic/ 154 | [Rr]elease/ 155 | [Rr]eleases/ 156 | x64/ 157 | x86/ 158 | [Ww][Ii][Nn]32/ 159 | [Aa][Rr][Mm]/ 160 | [Aa][Rr][Mm]64/ 161 | bld/ 162 | [Bb]in/ 163 | [Oo]bj/ 164 | [Ll]og/ 165 | [Ll]ogs/ 166 | 167 | # Visual Studio 2015/2017 cache/options directory 168 | .vs/ 169 | # Uncomment if you have tasks that create the project's static files in wwwroot 170 | #wwwroot/ 171 | 172 | # Visual Studio 2017 auto generated files 173 | Generated\ Files/ 174 | 175 | # MSTest test Results 176 | [Tt]est[Rr]esult*/ 177 | [Bb]uild[Ll]og.* 178 | 179 | # NUnit 180 | *.VisualState.xml 181 | TestResult.xml 182 | nunit-*.xml 183 | 184 | # Build Results of an ATL Project 185 | [Dd]ebugPS/ 186 | [Rr]eleasePS/ 187 | dlldata.c 188 | 189 | # Benchmark Results 190 | BenchmarkDotNet.Artifacts/ 191 | 192 | # .NET Core 193 | project.lock.json 194 | project.fragment.lock.json 195 | artifacts/ 196 | 197 | # ASP.NET Scaffolding 198 | ScaffoldingReadMe.txt 199 | 200 | # StyleCop 201 | StyleCopReport.xml 202 | 203 | # Files built by Visual Studio 204 | *_i.c 205 | *_p.c 206 | *_h.h 207 | *.ilk 208 | *.meta 209 | *.obj 210 | *.iobj 211 | *.pch 212 | *.pdb 213 | *.ipdb 214 | *.pgc 215 | *.pgd 216 | *.rsp 217 | *.sbr 218 | *.tlb 219 | *.tli 220 | *.tlh 221 | *.tmp 222 | *.tmp_proj 223 | *_wpftmp.csproj 224 | *.log 225 | *.tlog 226 | *.vspscc 227 | *.vssscc 228 | .builds 229 | *.pidb 230 | *.svclog 231 | *.scc 232 | 233 | # Chutzpah Test files 234 | _Chutzpah* 235 | 236 | # Visual C++ cache files 237 | ipch/ 238 | *.aps 239 | *.ncb 240 | *.opendb 241 | *.opensdf 242 | *.sdf 243 | *.cachefile 244 | *.VC.db 245 | *.VC.VC.opendb 246 | 247 | # Visual Studio profiler 248 | *.psess 249 | *.vsp 250 | *.vspx 251 | *.sap 252 | 253 | # Visual Studio Trace Files 254 | *.e2e 255 | 256 | # TFS 2012 Local Workspace 257 | $tf/ 258 | 259 | # Guidance Automation Toolkit 260 | *.gpState 261 | 262 | # ReSharper is a .NET coding add-in 263 | _ReSharper*/ 264 | *.[Rr]e[Ss]harper 265 | *.DotSettings.user 266 | 267 | # TeamCity is a build add-in 268 | _TeamCity* 269 | 270 | # DotCover is a Code Coverage Tool 271 | *.dotCover 272 | 273 | # AxoCover is a Code Coverage Tool 274 | .axoCover/* 275 | !.axoCover/settings.json 276 | 277 | # Coverlet is a free, cross platform Code Coverage Tool 278 | coverage*.json 279 | coverage*.xml 280 | coverage*.info 281 | 282 | # Visual Studio code coverage results 283 | *.coverage 284 | *.coveragexml 285 | 286 | # NCrunch 287 | _NCrunch_* 288 | .*crunch*.local.xml 289 | nCrunchTemp_* 290 | 291 | # MightyMoose 292 | *.mm.* 293 | AutoTest.Net/ 294 | 295 | # Web workbench (sass) 296 | .sass-cache/ 297 | 298 | # Installshield output folder 299 | [Ee]xpress/ 300 | 301 | # DocProject is a documentation generator add-in 302 | DocProject/buildhelp/ 303 | DocProject/Help/*.HxT 304 | DocProject/Help/*.HxC 305 | DocProject/Help/*.hhc 306 | DocProject/Help/*.hhk 307 | DocProject/Help/*.hhp 308 | DocProject/Help/Html2 309 | DocProject/Help/html 310 | 311 | # Click-Once directory 312 | publish/ 313 | 314 | # Publish Web Output 315 | *.[Pp]ublish.xml 316 | *.azurePubxml 317 | # Note: Comment the next line if you want to checkin your web deploy settings, 318 | # but database connection strings (with potential passwords) will be unencrypted 319 | *.pubxml 320 | *.publishproj 321 | 322 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 323 | # checkin your Azure Web App publish settings, but sensitive information contained 324 | # in these scripts will be unencrypted 325 | PublishScripts/ 326 | 327 | # NuGet Packages 328 | *.nupkg 329 | # NuGet Symbol Packages 330 | *.snupkg 331 | # The packages folder can be ignored because of Package Restore 332 | **/[Pp]ackages/* 333 | # except build/, which is used as an MSBuild target. 334 | !**/[Pp]ackages/build/ 335 | # Uncomment if necessary however generally it will be regenerated when needed 336 | #!**/[Pp]ackages/repositories.config 337 | # NuGet v3's project.json files produces more ignorable files 338 | *.nuget.props 339 | *.nuget.targets 340 | 341 | # Nuget personal access tokens and Credentials 342 | nuget.config 343 | 344 | # Microsoft Azure Build Output 345 | csx/ 346 | *.build.csdef 347 | 348 | # Microsoft Azure Emulator 349 | ecf/ 350 | rcf/ 351 | 352 | # Windows Store app package directories and files 353 | AppPackages/ 354 | BundleArtifacts/ 355 | Package.StoreAssociation.xml 356 | _pkginfo.txt 357 | *.appx 358 | *.appxbundle 359 | *.appxupload 360 | 361 | # Visual Studio cache files 362 | # files ending in .cache can be ignored 363 | *.[Cc]ache 364 | # but keep track of directories ending in .cache 365 | !?*.[Cc]ache/ 366 | 367 | # Others 368 | ClientBin/ 369 | ~$* 370 | *~ 371 | *.dbmdl 372 | *.dbproj.schemaview 373 | *.jfm 374 | *.pfx 375 | *.publishsettings 376 | orleans.codegen.cs 377 | 378 | # Including strong name files can present a security risk 379 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 380 | #*.snk 381 | 382 | # Since there are multiple workflows, uncomment next line to ignore bower_components 383 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 384 | #bower_components/ 385 | 386 | # RIA/Silverlight projects 387 | Generated_Code/ 388 | 389 | # Backup & report files from converting an old project file 390 | # to a newer Visual Studio version. Backup files are not needed, 391 | # because we have git ;-) 392 | _UpgradeReport_Files/ 393 | Backup*/ 394 | UpgradeLog*.XML 395 | UpgradeLog*.htm 396 | ServiceFabricBackup/ 397 | *.rptproj.bak 398 | 399 | # SQL Server files 400 | *.mdf 401 | *.ldf 402 | *.ndf 403 | 404 | # Business Intelligence projects 405 | *.rdl.data 406 | *.bim.layout 407 | *.bim_*.settings 408 | *.rptproj.rsuser 409 | *- [Bb]ackup.rdl 410 | *- [Bb]ackup ([0-9]).rdl 411 | *- [Bb]ackup ([0-9][0-9]).rdl 412 | 413 | # Microsoft Fakes 414 | FakesAssemblies/ 415 | 416 | # GhostDoc plugin setting file 417 | *.GhostDoc.xml 418 | 419 | # Node.js Tools for Visual Studio 420 | .ntvs_analysis.dat 421 | node_modules/ 422 | 423 | # Visual Studio 6 build log 424 | *.plg 425 | 426 | # Visual Studio 6 workspace options file 427 | *.opt 428 | 429 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 430 | *.vbw 431 | 432 | # Visual Studio LightSwitch build output 433 | **/*.HTMLClient/GeneratedArtifacts 434 | **/*.DesktopClient/GeneratedArtifacts 435 | **/*.DesktopClient/ModelManifest.xml 436 | **/*.Server/GeneratedArtifacts 437 | **/*.Server/ModelManifest.xml 438 | _Pvt_Extensions 439 | 440 | # Paket dependency manager 441 | .paket/paket.exe 442 | paket-files/ 443 | 444 | # FAKE - F# Make 445 | .fake/ 446 | 447 | # CodeRush personal settings 448 | .cr/personal 449 | 450 | # Python Tools for Visual Studio (PTVS) 451 | __pycache__/ 452 | *.pyc 453 | 454 | # Cake - Uncomment if you are using it 455 | # tools/** 456 | # !tools/packages.config 457 | 458 | # Tabs Studio 459 | *.tss 460 | 461 | # Telerik's JustMock configuration file 462 | *.jmconfig 463 | 464 | # BizTalk build output 465 | *.btp.cs 466 | *.btm.cs 467 | *.odx.cs 468 | *.xsd.cs 469 | 470 | # OpenCover UI analysis results 471 | OpenCover/ 472 | 473 | # Azure Stream Analytics local run output 474 | ASALocalRun/ 475 | 476 | # MSBuild Binary and Structured Log 477 | *.binlog 478 | 479 | # NVidia Nsight GPU debugger configuration file 480 | *.nvuser 481 | 482 | # MFractors (Xamarin productivity tool) working folder 483 | .mfractor/ 484 | 485 | # Local History for Visual Studio 486 | .localhistory/ 487 | 488 | # BeatPulse healthcheck temp database 489 | healthchecksdb 490 | 491 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 492 | MigrationBackup/ 493 | 494 | # Ionide (cross platform F# VS Code tools) working folder 495 | .ionide/ 496 | 497 | # Fody - auto-generated XML schema 498 | FodyWeavers.xsd 499 | 500 | # VS Code files for those working on multiple tools 501 | 502 | # Local History for Visual Studio Code 503 | 504 | # Windows Installer files from build outputs 505 | *.cab 506 | *.msi 507 | *.msix 508 | *.msm 509 | *.msp 510 | 511 | # JetBrains Rider 512 | *.sln.iml 513 | 514 | ### VisualStudio Patch ### 515 | # Additional files built by Visual Studio 516 | 517 | # End of https://www.toptal.com/developers/gitignore/api/sonarqube,intellij+all,visualstudio,visualstudiocode 518 | -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Build Schema", 4 | "$ref": "#/definitions/build", 5 | "definitions": { 6 | "build": { 7 | "type": "object", 8 | "properties": { 9 | "Configuration": { 10 | "type": "string", 11 | "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)" 12 | }, 13 | "Continue": { 14 | "type": "boolean", 15 | "description": "Indicates to continue a previously failed build attempt" 16 | }, 17 | "CoverletDiag": { 18 | "type": "boolean", 19 | "description": "Enable coverlet diagnostics (log.*.txt)" 20 | }, 21 | "Help": { 22 | "type": "boolean", 23 | "description": "Shows the help text for this build assembly" 24 | }, 25 | "Host": { 26 | "type": "string", 27 | "description": "Host for execution. Default is 'automatic'", 28 | "enum": [ 29 | "AppVeyor", 30 | "AzurePipelines", 31 | "Bamboo", 32 | "Bitrise", 33 | "GitHubActions", 34 | "GitLab", 35 | "Jenkins", 36 | "Rider", 37 | "SpaceAutomation", 38 | "TeamCity", 39 | "Terminal", 40 | "TravisCI", 41 | "VisualStudio", 42 | "VSCode" 43 | ] 44 | }, 45 | "IsCiBuild": { 46 | "type": "boolean", 47 | "description": "Is CI Build" 48 | }, 49 | "IsPushTag": { 50 | "type": "boolean", 51 | "description": "Push built NuGet package" 52 | }, 53 | "NoLogo": { 54 | "type": "boolean", 55 | "description": "Disables displaying the NUKE logo" 56 | }, 57 | "NuGetApiKey": { 58 | "type": "string", 59 | "description": "NuGet API Key" 60 | }, 61 | "NuGetSource": { 62 | "type": "string", 63 | "description": "NuGet Source" 64 | }, 65 | "Partition": { 66 | "type": "string", 67 | "description": "Partition to use on CI" 68 | }, 69 | "Plan": { 70 | "type": "boolean", 71 | "description": "Shows the execution plan (HTML)" 72 | }, 73 | "Profile": { 74 | "type": "array", 75 | "description": "Defines the profiles to load", 76 | "items": { 77 | "type": "string" 78 | } 79 | }, 80 | "Root": { 81 | "type": "string", 82 | "description": "Root directory during build execution" 83 | }, 84 | "Skip": { 85 | "type": "array", 86 | "description": "List of targets to be skipped. Empty list skips all dependencies", 87 | "items": { 88 | "type": "string", 89 | "enum": [ 90 | "CiBuild", 91 | "Clean", 92 | "Compile", 93 | "Coverage", 94 | "Package", 95 | "Push", 96 | "Restore", 97 | "Sonar", 98 | "SonarBegin", 99 | "SonarEnd", 100 | "Test" 101 | ] 102 | } 103 | }, 104 | "Solution": { 105 | "type": "string", 106 | "description": "Path to a solution file that is automatically loaded" 107 | }, 108 | "SonarLogin": { 109 | "type": "string", 110 | "description": "The SonarQube login token" 111 | }, 112 | "SonarOrganization": { 113 | "type": "string", 114 | "description": "The SonarQube organization" 115 | }, 116 | "SonarServer": { 117 | "type": "string", 118 | "description": "The SonarQube server" 119 | }, 120 | "Target": { 121 | "type": "array", 122 | "description": "List of targets to be invoked. Default is '{default_target}'", 123 | "items": { 124 | "type": "string", 125 | "enum": [ 126 | "CiBuild", 127 | "Clean", 128 | "Compile", 129 | "Coverage", 130 | "Package", 131 | "Push", 132 | "Restore", 133 | "Sonar", 134 | "SonarBegin", 135 | "SonarEnd", 136 | "Test" 137 | ] 138 | } 139 | }, 140 | "Verbosity": { 141 | "type": "string", 142 | "description": "Logging verbosity during build execution. Default is 'Normal'", 143 | "enum": [ 144 | "Minimal", 145 | "Normal", 146 | "Quiet", 147 | "Verbose" 148 | ] 149 | } 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "FluentAssertions.Autofac.sln" 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | # .prettierrc or .prettierrc.yaml 2 | trailingComma: "es5" 3 | tabWidth: 2 4 | semi: false 5 | singleQuote: true 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.schemas": { 3 | "https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "generalSettings": { 3 | "shouldScanRepo": true 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "success" 7 | } 8 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love contributions. To get started contributing you might need: 4 | 5 | - [Get started with git](http://rogerdudler.github.io/git-guide) 6 | - [How to create a pull request](https://help.github.com/articles/using-pull-requests) 7 | - [An issue to work on](https://github.com/fluentassertions/FluentAssertions.Autofac/labels/up-for-grabs) - We are 8 | on [Up for grabs](http://up-for-grabs.net/), our up for grabs issues are tagged `up-for-grabs` 9 | - An understanding of how [we write tests](#writing-tests) 10 | 11 | Once you know how to create a pull request and have an issue to work on, just post a comment saying you will work on it. 12 | If you end up not being able to complete the task, please post another comment so others can pick it up. 13 | 14 | Issues are also welcome, [failing tests](#writing-tests) are even more welcome. 15 | 16 | ## Contribution Guidelines 17 | 18 | - Try to use feature branches rather than developing on main 19 | - Please include tests covering the change 20 | - The docs are now stored in the repository under the `Docs` folder, please include documentation updates with your PR 21 | 22 | ## Writing Tests 23 | 24 | It is easy to write tests in `FluentAssertions.Autofac`. Test fixtures are located directly beside the code in classes 25 | ending with `_Should.cs`. 26 | 27 | ### 1. Find appropriate fixture 28 | 29 | Find where your issue would logically sit, i.e. find the class closest to your issue. 30 | 31 | ### 2. Create a test method 32 | 33 | We are currently using xUnit, so just create a descriptive test method and attribute it with `[Fact]`. 34 | 35 | ### 3. Submit a pull request with the failing test 36 | 37 | Even better include the fix, but a failing test is a great start . 38 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/AutofacAssertionExtensions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class AutofacAssertionExtensions_Should 10 | { 11 | [Fact] 12 | public void Provide_builder_extension() 13 | { 14 | var builder = new ContainerBuilder(); 15 | builder.Should().ShouldBeOfType(); 16 | } 17 | 18 | [Fact] 19 | public void Provide_register_extension() 20 | { 21 | var container = new ContainerBuilder().Build(); 22 | container.Should().Have().ShouldBeOfType(); 23 | } 24 | 25 | [Fact] 26 | public void Provide_register_extension_on_lifetime_scope() 27 | { 28 | var scope = new ContainerBuilder().Build().BeginLifetimeScope(); 29 | scope.Should().Have().ShouldBeOfType(); 30 | } 31 | 32 | [Fact] 33 | public void Provide_resolve_extension() 34 | { 35 | var builder = new ContainerBuilder(); 36 | builder.RegisterInstance(Substitute.For()); 37 | var container = builder.Build(); 38 | container.Should().Resolve().ShouldBeOfType(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/AutofacExtensions_Should.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Autofac; 3 | using Autofac.Core; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class AutofacExtensions_Should 10 | { 11 | [Fact] 12 | public void Get_Registrations() 13 | { 14 | var builder = new ContainerBuilder(); 15 | const string expected = "hello"; 16 | builder.RegisterInstance(expected); 17 | var container = builder.Build(); 18 | 19 | var registration = container.ComponentRegistry.GetRegistration(); 20 | registration.Activator.LimitType.Should().Be(); 21 | var service = (TypedService)registration.Services.Single(); 22 | service.ServiceType.Should().Be(); 23 | 24 | var actual = container.Resolve(); 25 | actual.Should().Be(expected); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/BuilderAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Xunit; 3 | 4 | namespace FluentAssertions.Autofac; 5 | 6 | // ReSharper disable InconsistentNaming 7 | public class BuilderAssertions_Should 8 | { 9 | [Fact] 10 | public void Support_testing_assembly_modules_registrations() 11 | { 12 | var builder = new ContainerBuilder(); 13 | 14 | var assembly = typeof(BuilderAssertions_Should) 15 | .Assembly; 16 | builder.RegisterAssemblyModules(assembly); 17 | 18 | builder.Should().RegisterAssemblyModules(assembly); 19 | builder.Should().RegisterModule(); 20 | builder.Should().RegisterModule(); 21 | } 22 | 23 | [Fact] 24 | public void Support_testing_module_registration() 25 | { 26 | var builder = new ContainerBuilder(); 27 | builder.RegisterModule(); 28 | 29 | var builderShould = builder.Should(); 30 | builderShould.RegisterModule(); 31 | builderShould.RegisterModule(typeof(SampleModule)); 32 | 33 | builderShould.RegisterModule(); 34 | } 35 | 36 | // ReSharper disable once MemberCanBePrivate.Global 37 | public class SampleModule : Module 38 | { 39 | protected override void Load(ContainerBuilder builder) 40 | { 41 | builder.RegisterModule(); 42 | } 43 | } 44 | 45 | // ReSharper disable once MemberCanBePrivate.Global 46 | public class SampleModule2 : Module 47 | { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/ContainerAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class ContainerAssertions_Should 10 | { 11 | [Fact] 12 | public void Provide_assertions() 13 | { 14 | var builder = new ContainerBuilder(); 15 | builder.RegisterInstance(Substitute.For()); 16 | builder.RegisterType().AutoActivate(); 17 | builder.RegisterType().AsImplementedInterfaces(); 18 | var container = builder.Build(); 19 | 20 | var containerShould = container.Should(); 21 | 22 | containerShould.ShouldBeOfType(); 23 | containerShould.Have().ShouldBeOfType(); 24 | containerShould.Resolve().ShouldBeOfType(); 25 | containerShould.Resolve(typeof(IDisposable)).ShouldBeOfType(); 26 | 27 | containerShould.AutoActivate(); 28 | //containerShould.AutoActivate(); 29 | containerShould.Resolve().As(); 30 | containerShould.Have().Registered().As(); 31 | } 32 | 33 | // ReSharper disable ClassNeverInstantiated.Local 34 | private class AutoActivateService 35 | { 36 | } 37 | 38 | private class AutoActivateService2 : IStartable 39 | { 40 | // ReSharper restore ClassNeverInstantiated.Local 41 | public void Start() { } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/ContainerRegistrationAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class ContainerRegistrationAssertions_Should 10 | { 11 | [Fact] 12 | public void RegisterType() 13 | { 14 | var sut = Configure(builder => builder.RegisterType()).Should().Have(); 15 | sut.Registered(); 16 | sut.Registered(typeof(Dummy)); 17 | } 18 | 19 | [Fact] 20 | public void RegisterInstance() 21 | { 22 | var instance = new Dummy(); 23 | var container = Configure(builder => builder.RegisterInstance(instance)); 24 | container.Should().Have().Registered(instance); 25 | } 26 | 27 | [Fact] 28 | public void NotRegister() 29 | { 30 | var sut = Configure().Should().Have(); 31 | sut.NotRegistered(); 32 | sut.NotRegistered(typeof(IDisposable)); 33 | sut.NotRegistered("foo"); 34 | sut.NotRegistered("bar", typeof(IDisposable)); 35 | sut.NotRegistered(42); 36 | sut.NotRegistered(42, typeof(IDisposable)); 37 | } 38 | 39 | private static IContainer Configure(Action arrange = null) 40 | { 41 | var builder = new ContainerBuilder(); 42 | arrange?.Invoke(builder); 43 | return builder.Build(); 44 | } 45 | 46 | [ExcludeFromCodeCoverage] 47 | // ReSharper disable ClassNeverInstantiated.Local 48 | private class Dummy : IDisposable 49 | { 50 | public void Dispose() { } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/FluentAssertions.Autofac.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ..\ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | net6.0 13 | false 14 | FluentAssertions.Autofac 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | all 27 | runtime; build; native; contentfiles; analyzers; buildtransitive 28 | 29 | 30 | all 31 | runtime; build; native; contentfiles; analyzers; buildtransitive 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/Module_Should.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Autofac; 3 | using Xunit; 4 | 5 | namespace FluentAssertions.Autofac; 6 | 7 | // ReSharper disable InconsistentNaming 8 | public class Module_Should 9 | { 10 | [Fact] 11 | public void Provide_test_container() 12 | { 13 | var container = Module.GetTestContainer((builder, module) => 14 | { 15 | Trace.WriteLine($"Customizing '{builder}' and '{module}'."); 16 | }); 17 | container.Should().NotBeNull(); 18 | } 19 | 20 | [Fact] 21 | public void Provide_test_builder() 22 | { 23 | var builder = Module.GetTestBuilder((b, m) => 24 | { 25 | Trace.WriteLine($"Customizing '{b}' and '{m}'."); 26 | }); 27 | builder.Should().RegisterModule(); 28 | } 29 | 30 | private class SampleModule : Module 31 | { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/RegisterAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class RegisterAssertions_Should 10 | { 11 | [Fact] 12 | public void Register_Type_As() 13 | { 14 | var containerShouldHave = Configure(builder => 15 | builder.RegisterType() 16 | .AsSelf() 17 | .As() 18 | .AsImplementedInterfaces() 19 | ); 20 | 21 | AssertAsRegistrations(containerShouldHave.Registered()); 22 | } 23 | 24 | [Fact] 25 | public void Register_Instance() 26 | { 27 | var instance = new Dummy(); 28 | 29 | var containerShouldHave = Configure(builder => 30 | builder.RegisterInstance(instance) 31 | .AsSelf() 32 | .As() 33 | .AsImplementedInterfaces()); 34 | 35 | AssertAsRegistrations(containerShouldHave.Registered(instance)); 36 | } 37 | 38 | private static ContainerRegistrationAssertions Configure(Action arrange = null) 39 | { 40 | var builder = new ContainerBuilder(); 41 | arrange?.Invoke(builder); 42 | return builder.Build().Should().Have(); 43 | } 44 | 45 | private static void AssertAsRegistrations(RegisterAssertions dummyShouldBeRegistered) 46 | { 47 | dummyShouldBeRegistered 48 | .AsSelf() 49 | .As() 50 | .As(typeof(IDisposable)) 51 | .As(typeof(IDisposable), typeof(Dummy)) 52 | .AsImplementedInterfaces(); 53 | } 54 | 55 | [ExcludeFromCodeCoverage] 56 | private class Dummy : IDisposable 57 | { 58 | public void Dispose() { } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/RegisterGenericSourceAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class RegisterGenericSourceAssertions_Should 10 | { 11 | [Fact] 12 | public void Register_Generic() 13 | { 14 | var containerShouldHave = GetSut(builder => 15 | builder.RegisterGeneric(typeof(Repository<>)) 16 | .As(typeof(IRepository<>)) 17 | .SingleInstance() 18 | ); 19 | 20 | containerShouldHave 21 | .RegisteredGeneric(typeof(Repository<>)) 22 | .As(typeof(IRepository<>)) 23 | .SingleInstance(); 24 | } 25 | 26 | [Fact] 27 | public void Register_Named_Generic() 28 | { 29 | var containerShouldHave = GetSut(builder => 30 | builder.RegisterGeneric(typeof(Repository<>)) 31 | .Named("awesomeRepo", typeof(IRepository<>)) 32 | .SingleInstance() 33 | ); 34 | 35 | containerShouldHave 36 | .RegisteredGeneric(typeof(Repository<>)) 37 | .Named("awesomeRepo", typeof(IRepository<>)) 38 | .SingleInstance(); 39 | } 40 | 41 | [Fact] 42 | public void Register_Generic_WithMultiple_GenericArgumentTypes() 43 | { 44 | var containerShouldHave = GetSut(builder => 45 | builder.RegisterGeneric(typeof(MultipleRepository<,>)) 46 | .As(typeof(IMultipleRepository<,>)) 47 | .SingleInstance() 48 | ); 49 | 50 | containerShouldHave 51 | .RegisteredGeneric(typeof(MultipleRepository<,>)) 52 | .As(typeof(IMultipleRepository<,>)) 53 | .SingleInstance(); 54 | } 55 | 56 | [Fact] 57 | public void Register_Generic_ShouldThrow_ArgumentNullException_WhenGenericComponentTypeDefinition_IsNull() 58 | { 59 | AssertRegisterGenericThrows(null, typeof(IRepository<>)); 60 | } 61 | 62 | [Fact] 63 | public void Register_Generic_ShouldThrow_ArgumentException_WhenGenericComponentTypeDefinition_IsNotGenericType() 64 | { 65 | AssertRegisterGenericThrows(typeof(NotGenericRepository), typeof(IRepository<>)); 66 | } 67 | 68 | [Fact] 69 | public void 70 | Register_Generic_ShouldThrow_ArgumentException_WhenGenericComponentTypeDefinition_IsNotGenericTypeDefinition() 71 | { 72 | AssertRegisterGenericThrows(typeof(IRepository), typeof(IRepository<>)); 73 | } 74 | 75 | [Fact] 76 | public void Register_Generic_ShouldThrow_ArgumentNullException_WhenGenericServiceTypeDefinition_IsNull() 77 | { 78 | AssertRegisterGenericThrows(typeof(IRepository<>), null); 79 | } 80 | 81 | [Fact] 82 | public void Register_Generic_ShouldThrow_ArgumentException_WhenGenericServiceTypeDefinition_IsNotGenericType() 83 | { 84 | AssertRegisterGenericThrows(typeof(IRepository<>), typeof(IRepository)); 85 | } 86 | 87 | [Fact] 88 | public void 89 | Register_Generic_ShouldThrow_ArgumentException_WhenGenericServiceTypeDefinition_IsNotGenericTypeDefinition() 90 | { 91 | AssertRegisterGenericThrows(typeof(IRepository<>), typeof(IRepository)); 92 | } 93 | 94 | private static void AssertRegisterGenericThrows(Type componentType, Type serviceType) 95 | { 96 | AssertRegisterGenericThrows(componentType, serviceType); 97 | } 98 | 99 | private static void AssertRegisterGenericThrows(Type componentType, Type serviceType) 100 | where TException : ArgumentException 101 | { 102 | var containerShouldHave = GetSut(builder => 103 | builder.RegisterGeneric(typeof(Repository<>)) 104 | .As(typeof(IRepository<>)) 105 | .SingleInstance() 106 | ); 107 | 108 | containerShouldHave.Invoking(x => x 109 | .RegisteredGeneric(componentType) 110 | .As(serviceType) 111 | .SingleInstance() 112 | ).Should().Throw(); 113 | } 114 | 115 | private static ContainerRegistrationAssertions GetSut(Action arrange = null) 116 | { 117 | var builder = new ContainerBuilder(); 118 | arrange?.Invoke(builder); 119 | return builder.Build().Should().Have(); 120 | } 121 | 122 | private interface IRepository 123 | { 124 | } 125 | 126 | [ExcludeFromCodeCoverage] 127 | private class Repository : IRepository 128 | { 129 | } 130 | 131 | [ExcludeFromCodeCoverage] 132 | private class MultipleRepository : IMultipleRepository 133 | { 134 | } 135 | 136 | [ExcludeFromCodeCoverage] 137 | private class NotGenericRepository : IRepository 138 | { 139 | } 140 | 141 | // ReSharper disable UnusedTypeParameter 142 | private interface IRepository : IRepository 143 | { 144 | } 145 | 146 | private interface IMultipleRepository : IRepository 147 | { 148 | } 149 | // ReSharper restore UnusedTypeParameter 150 | } 151 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/RegistrationAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using Autofac.Core; 5 | using Xunit; 6 | 7 | namespace FluentAssertions.Autofac; 8 | 9 | // ReSharper disable InconsistentNaming 10 | public class RegistrationAssertions_Should 11 | { 12 | [Fact] 13 | public void Register_Named() 14 | { 15 | var containerShouldHave = GetSut(builder => 16 | builder.RegisterType().Named("Dummy") 17 | ); 18 | 19 | containerShouldHave.Registered() 20 | .Named("Dummy") 21 | .Keyed("Dummy"); 22 | } 23 | 24 | [Fact] 25 | public void Register_Keyed() 26 | { 27 | var containerShouldHave = GetSut(builder => 28 | builder.RegisterType().Keyed(42) 29 | ); 30 | 31 | containerShouldHave.Registered() 32 | .Keyed(42); 33 | } 34 | 35 | [Fact] 36 | public void Register_Lifetime() 37 | { 38 | var containerShouldHave = GetSut(builder => builder.RegisterType().SingleInstance()); 39 | containerShouldHave.Registered().SingleInstance(); 40 | 41 | containerShouldHave = GetSut(builder => builder.RegisterType().InstancePerDependency()); 42 | containerShouldHave.Registered().InstancePerDependency(); 43 | 44 | containerShouldHave = GetSut(builder => builder.RegisterType().InstancePerLifetimeScope()); 45 | containerShouldHave.Registered().InstancePerLifetimeScope(); 46 | 47 | containerShouldHave = GetSut(builder => builder.RegisterType().InstancePerMatchingLifetimeScope()); 48 | containerShouldHave.Registered().InstancePerMatchingLifetimeScope(); 49 | 50 | containerShouldHave = GetSut(builder => builder.RegisterType().InstancePerRequest()); 51 | containerShouldHave.Registered().InstancePerRequest(); 52 | 53 | containerShouldHave = GetSut(builder => builder.RegisterType().InstancePerOwned()); 54 | containerShouldHave.Registered().InstancePerOwned(); 55 | } 56 | 57 | [Fact] 58 | public void Register_Ownership() 59 | { 60 | var containerShouldHave = GetSut(builder => builder.RegisterType().ExternallyOwned()); 61 | containerShouldHave.Registered() 62 | .ExternallyOwned() 63 | .Owned(InstanceOwnership.ExternallyOwned); 64 | 65 | containerShouldHave = GetSut(builder => builder.RegisterType().OwnedByLifetimeScope()); 66 | containerShouldHave.Registered() 67 | .OwnedByLifetimeScope() 68 | .Owned(InstanceOwnership.OwnedByLifetimeScope); 69 | } 70 | 71 | [Fact] 72 | public void Register_AutoActivate() 73 | { 74 | var containerShouldHave = GetSut(builder => builder.RegisterType().As().AutoActivate()); 75 | containerShouldHave.Registered().As().AutoActivate(); 76 | } 77 | 78 | [Fact] 79 | public void Register_parameters() 80 | { 81 | var builder = new ContainerBuilder(); 82 | 83 | const string paramName = "name"; 84 | const string paramValue = "Name"; 85 | 86 | builder.RegisterType() 87 | .As() 88 | .WithParameter(paramName, paramValue) 89 | .WithParameter(new NamedParameter(paramName, paramValue)) 90 | .WithParameter(new PositionalParameter(0, paramValue)); 91 | 92 | var container = builder.Build(); 93 | container.Should().Have() 94 | .Registered() 95 | .As() 96 | .WithParameter(paramName, paramValue) 97 | .WithParameter(new NamedParameter(paramName, paramValue)) 98 | .WithParameter(new PositionalParameter(0, paramValue)) 99 | ; 100 | } 101 | 102 | [Fact] 103 | public void Register_parametersMatchingPredicate() 104 | { 105 | var builder = new ContainerBuilder(); 106 | 107 | builder.RegisterType() 108 | .As() 109 | .WithParameter(new TypedParameter(typeof(string), "stringValue")) 110 | .WithParameter(new TypedParameter(typeof(int), "intValue")); 111 | 112 | static bool IsString(Parameter p) => p is TypedParameter tp 113 | #pragma warning disable CS0252 // Possible unintended reference comparison; left hand side needs cast 114 | && tp.Type == typeof(string) && tp.Value == "stringValue"; 115 | #pragma warning restore CS0252 // Possible unintended reference comparison; left hand side needs cast 116 | 117 | static bool IsTyped(Parameter p) 118 | { 119 | return p is TypedParameter; 120 | } 121 | 122 | var container = builder.Build(); 123 | container.Should().Have() 124 | .Registered() 125 | .As() 126 | .WithParameter(IsString) 127 | .WithParameter(IsTyped, 2); 128 | } 129 | 130 | private static ContainerRegistrationAssertions GetSut(Action arrange = null) 131 | { 132 | var builder = new ContainerBuilder(); 133 | arrange?.Invoke(builder); 134 | return builder.Build().Should().Have(); 135 | } 136 | 137 | [ExcludeFromCodeCoverage] 138 | // ReSharper disable ClassNeverInstantiated.Local 139 | private class Dummy : IDisposable 140 | { 141 | public void Dispose() { } 142 | } 143 | 144 | [ExcludeFromCodeCoverage] 145 | private class NamedDummy : IDisposable 146 | { 147 | public void Dispose() { } 148 | } 149 | 150 | [ExcludeFromCodeCoverage] 151 | private class KeyedDummy : IComparable 152 | { 153 | public int CompareTo(object obj) { return 42; } 154 | } 155 | 156 | [ExcludeFromCodeCoverage] 157 | // ReSharper disable once UnusedMember.Local 158 | // ReSharper disable once UnusedType.Local 159 | private class ParameterizedDummy : Dummy 160 | { 161 | public ParameterizedDummy(string name) { Name = name; } 162 | 163 | // ReSharper disable once MemberCanBePrivate.Local 164 | // ReSharper disable once UnusedAutoPropertyAccessor.Local 165 | public string Name { get; } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/ResolveAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using NSubstitute; 5 | using Xunit; 6 | 7 | namespace FluentAssertions.Autofac; 8 | 9 | // ReSharper disable InconsistentNaming 10 | public class ResolveAssertions_Should 11 | { 12 | [Fact] 13 | public void Resolve() 14 | { 15 | var container = Configure(); 16 | container.Invoking(x => x.Should().Resolve()) 17 | .Should().Throw() 18 | .WithMessage($"Expected container to resolve '{typeof(IDisposable)}' but it did not."); 19 | 20 | var disposable = Substitute.For(); 21 | container = Configure(builder => builder.RegisterInstance(disposable)); 22 | container.Should().Resolve(); 23 | } 24 | 25 | [Fact] 26 | public void Resolve_As() 27 | { 28 | var containerShould = Configure(builder => 29 | builder.RegisterType() 30 | .AsSelf() 31 | .As() 32 | ).Should(); 33 | 34 | containerShould.Resolve(); 35 | containerShould.Resolve().AsSelf(); 36 | containerShould.Resolve().As(); 37 | containerShould.Resolve().As(typeof(Dummy)); 38 | } 39 | 40 | [Fact] 41 | public void Resolve_Named() 42 | { 43 | var containerShould = Configure(builder => 44 | builder.RegisterType() 45 | .Named("dummy") 46 | .AsSelf() 47 | ).Should(); 48 | 49 | containerShould.Resolve() 50 | .AsSelf() 51 | .Named("dummy") 52 | .Named("dummy", typeof(IDisposable)); 53 | } 54 | 55 | [Fact] 56 | public void Assert_AutoActivation() 57 | { 58 | var container = Configure(builder => builder.RegisterType().AsSelf().AutoActivate()); 59 | container.Should().Resolve().AutoActivate(); 60 | 61 | container = Configure(builder => builder.RegisterType().AutoActivate()); 62 | container.Should().AutoActivate(); 63 | container.Should().Invoking(x => x.Resolve()).Should() 64 | .Throw("type not registered AS something"); 65 | } 66 | 67 | private static IContainer Configure(Action arrange = null) 68 | { 69 | var builder = new ContainerBuilder(); 70 | arrange?.Invoke(builder); 71 | return builder.Build(); 72 | } 73 | 74 | [ExcludeFromCodeCoverage] 75 | private class Dummy : IDisposable 76 | { 77 | public void Dispose() { } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/TestExtensions_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | // ReSharper disable InconsistentNaming 9 | public class TestExtensions_Should 10 | { 11 | [Fact] 12 | public void Provide_builder() 13 | { 14 | var module = new SampleModule(); 15 | 16 | var builder = module.BuilderFor(); 17 | builder.Should().ShouldBeOfType(); 18 | 19 | builder.RegisterInstance(Substitute.For()); 20 | builder.RegisterInstance(Substitute.For()); 21 | builder.RegisterInstance(Substitute.For()); 22 | 23 | var container = builder.Build(); 24 | 25 | container.Resolve().Should().NotBeNull(); 26 | container.Resolve().Should().NotBeNull(); 27 | container.Resolve().Should().NotBeNull(); 28 | container.Resolve().Should().NotBeNull(); 29 | } 30 | 31 | [Fact] 32 | public void Provide_generic_module_builder() 33 | { 34 | var builder = Module.GetTestBuilder(b => 35 | { 36 | // register module dependency substitutes here! 37 | b.RegisterInstance(Substitute.For()); 38 | }); 39 | builder.Should().RegisterModule(); 40 | 41 | var container = builder.Build(); 42 | 43 | container.Resolve().Should().NotBeNull(); 44 | container.Resolve().Should().NotBeNull(); 45 | } 46 | 47 | [Fact] 48 | public void Provide_container() 49 | { 50 | var module = new SampleModule(); 51 | 52 | var container = module.Container(builder => 53 | { 54 | builder.RegisterInstance(Substitute.For()); 55 | builder.RegisterInstance(Substitute.For()); 56 | }); 57 | 58 | container.Resolve().Should().NotBeNull(); 59 | container.Resolve().Should().NotBeNull(); 60 | container.Resolve().Should().NotBeNull(); 61 | } 62 | 63 | private class SampleModule : Module 64 | { 65 | protected override void Load(ContainerBuilder builder) 66 | { 67 | builder.RegisterInstance(Substitute.For()); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/TestHelper.cs: -------------------------------------------------------------------------------- 1 | namespace FluentAssertions.Autofac; 2 | 3 | internal static class TestHelper 4 | { 5 | public static void ShouldBeOfType(this object value) 6 | { 7 | value.Should().BeOfType(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.Tests/TypeScanningAssertions_Should.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Xunit; 3 | 4 | namespace FluentAssertions.Autofac; 5 | 6 | // ReSharper disable InconsistentNaming 7 | public class TypeScanningAssertions_Should 8 | { 9 | [Fact] 10 | public void Support_type_scanning() 11 | { 12 | var builder = new ContainerBuilder(); 13 | builder.RegisterAssemblyTypes(typeof(IDummy).Assembly) 14 | .Where(t => t.IsAssignableTo()) 15 | .Except() 16 | .AsSelf() 17 | .AsImplementedInterfaces() 18 | .As() 19 | .As(t => t.GetInterfaces()[0]); 20 | 21 | var container = builder.Build(); 22 | var types = 23 | container.Should().RegisterAssemblyTypes(typeof(IDummy).Assembly) 24 | .Where(t => t.IsAssignableTo()) 25 | .Except() 26 | .AsSelf() 27 | .AsImplementedInterfaces() 28 | .As() 29 | .As(t => t.GetInterfaces()[0]) 30 | .Types; 31 | 32 | container.Should().RegisterTypes(types) 33 | .AsSelf() 34 | .AsImplementedInterfaces() 35 | .As() 36 | .As(t => t.GetInterfaces()[0]); 37 | } // ReSharper disable ClassNeverInstantiated.Local 38 | private interface IDummy 39 | { 40 | } 41 | 42 | // ReSharper disable UnusedType.Local 43 | private class Dummy1 : IDummy 44 | { 45 | } 46 | 47 | private class Dummy2 : IDummy 48 | { 49 | } 50 | // ReSharper restore UnusedType.Local 51 | 52 | private class Dummy3 : IDummy 53 | { 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29709.97 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{915DFDB6-990E-4068-899E-F84163AC7A9D}" 7 | ProjectSection(SolutionItems) = preProject 8 | .editorconfig = .editorconfig 9 | .gitattributes = .gitattributes 10 | .gitignore = .gitignore 11 | .nuke = .nuke 12 | .whitesource = .whitesource 13 | appveyor.yml = appveyor.yml 14 | azure-pipelines.yml = azure-pipelines.yml 15 | build.cmd = build.cmd 16 | build.ps1 = build.ps1 17 | build.sh = build.sh 18 | CONTRIBUTING.md = CONTRIBUTING.md 19 | FluentAssertions.Autofac.v3.ncrunchsolution = FluentAssertions.Autofac.v3.ncrunchsolution 20 | GitVersion.yml = GitVersion.yml 21 | global.json = global.json 22 | key.public = key.public 23 | key.snk = key.snk 24 | LICENSE = LICENSE 25 | README.md = README.md 26 | solution.targets = solution.targets 27 | SolutionInfo.cs = SolutionInfo.cs 28 | EndProjectSection 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.Autofac", "FluentAssertions.Autofac\FluentAssertions.Autofac.csproj", "{51CBAE5D-8DC3-4314-87A0-F97D058FD93D}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.Autofac.Tests", "FluentAssertions.Autofac.Tests\FluentAssertions.Autofac.Tests.csproj", "{849ECBE8-53FB-4EC9-AAC7-1E028DA76F0D}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleLib", "SampleLib\SampleLib.csproj", "{0AC7EB5C-948C-4636-A853-8E6FBC39E862}" 35 | EndProject 36 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_docs", "_docs", "{19E4231F-F592-4CF4-A00A-3477066288E3}" 37 | ProjectSection(SolutionItems) = preProject 38 | _docs\index.md = _docs\index.md 39 | _docs\usage.md = _docs\usage.md 40 | _docs\why.md = _docs\why.md 41 | EndProjectSection 42 | EndProject 43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{8C1612F0-6C37-4673-892B-95299B8099A2}" 44 | EndProject 45 | Global 46 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 47 | Debug|Any CPU = Debug|Any CPU 48 | Release|Any CPU = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 51 | {51CBAE5D-8DC3-4314-87A0-F97D058FD93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {51CBAE5D-8DC3-4314-87A0-F97D058FD93D}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {51CBAE5D-8DC3-4314-87A0-F97D058FD93D}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {51CBAE5D-8DC3-4314-87A0-F97D058FD93D}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {849ECBE8-53FB-4EC9-AAC7-1E028DA76F0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {849ECBE8-53FB-4EC9-AAC7-1E028DA76F0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {849ECBE8-53FB-4EC9-AAC7-1E028DA76F0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {849ECBE8-53FB-4EC9-AAC7-1E028DA76F0D}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {0AC7EB5C-948C-4636-A853-8E6FBC39E862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {0AC7EB5C-948C-4636-A853-8E6FBC39E862}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {0AC7EB5C-948C-4636-A853-8E6FBC39E862}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {0AC7EB5C-948C-4636-A853-8E6FBC39E862}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {8C1612F0-6C37-4673-892B-95299B8099A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {8C1612F0-6C37-4673-892B-95299B8099A2}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | EndGlobalSection 66 | GlobalSection(SolutionProperties) = preSolution 67 | HideSolutionNode = FALSE 68 | EndGlobalSection 69 | GlobalSection(ExtensibilityGlobals) = postSolution 70 | SolutionGuid = {197B6C02-F01C-4A0E-99AB-757EBF1FD78A} 71 | EndGlobalSection 72 | EndGlobal 73 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /FluentAssertions.Autofac.v3.ncrunchsolution: -------------------------------------------------------------------------------- 1 | 2 | 3 | True 4 | 5 | TargetFramework = net5.0 6 | DisableGitVersionTask = true 7 | 8 | True 9 | 10 | 11 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/AutofacAssertionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace FluentAssertions.Autofac; 4 | 5 | /// 6 | /// Contains extension methods for Autofac assertions. 7 | /// 8 | #if !DEBUG 9 | [System.Diagnostics.DebuggerNonUserCode] 10 | #endif 11 | public static class AutofacAssertionExtensions 12 | { 13 | /// 14 | /// Returns an object that can be used to assert the current 15 | /// (e.g. or ). 16 | /// 17 | public static ContainerAssertions Should(this IComponentContext container) 18 | { 19 | return new ContainerAssertions(container); 20 | } 21 | 22 | /// 23 | /// Returns an object that can be used to assert the current 24 | /// . 25 | /// 26 | public static BuilderAssertions Should(this ContainerBuilder builder) 27 | { 28 | return new BuilderAssertions(builder); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/AutofacExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using Autofac; 5 | using Autofac.Core; 6 | 7 | [assembly: 8 | InternalsVisibleTo( 9 | "FluentAssertions.Autofac.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d021d48ad15ae00f3d868ab095ef510f7757370a881a21578b0a36846b78af9d8c80968157f9a44d7b9861eb914b31cbd14d5e28317ab85f4aa32b21f7006cb8cae758954081835fc6c26ce2c4965dbb664e93e02b2f5b4bbface294ed968a22a8f02832cd065f1627af932fbb41229adc042ccdc9bed67487bbb7b17d2ff6")] 10 | 11 | namespace FluentAssertions.Autofac; 12 | 13 | internal static class AutofacExtensions 14 | { 15 | public static IComponentRegistration GetRegistration(this IComponentRegistry registry) 16 | { 17 | return GetRegistration(registry, typeof(T)); 18 | } 19 | 20 | public static IComponentRegistration GetRegistration(this IComponentRegistry registry, Type type) 21 | { 22 | var registration = registry.Registrations 23 | .FirstOrDefault(r => r.Activator.LimitType == type); 24 | 25 | registration.Should().NotBeNull($"Type '{type}' should be registered"); 26 | return registration; 27 | } 28 | 29 | public static void AssertAutoActivates(this IComponentRegistration registration, Type type) 30 | { 31 | registration.Services.Should() 32 | .Contain(service => service.Description == "AutoActivate", 33 | $"Type '{type}' should be auto activated"); 34 | } 35 | 36 | public static void AssertAutoActivates(this IComponentContext container, Type type) 37 | { 38 | var registration = container.ComponentRegistry.GetRegistration(type); 39 | AssertAutoActivates(registration, type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/BuilderAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Autofac; 7 | using Autofac.Builder; 8 | using Autofac.Core; 9 | using Autofac.Core.Registration; 10 | using FluentAssertions.Execution; 11 | using FluentAssertions.Primitives; 12 | using Module = Autofac.Module; 13 | 14 | namespace FluentAssertions.Autofac; 15 | 16 | /// 17 | /// 18 | /// Contains a number of methods to assert that an is in 19 | /// the expected state. 20 | /// 21 | #if !DEBUG 22 | [System.Diagnostics.DebuggerNonUserCode] 23 | #endif 24 | public class BuilderAssertions : ReferenceTypeAssertions 25 | { 26 | /// 27 | /// 28 | /// Returns the type of the subject the assertion applies on. 29 | /// 30 | [ExcludeFromCodeCoverage] 31 | protected override string Identifier => nameof(ContainerBuilder); 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// 37 | /// 38 | public BuilderAssertions(ContainerBuilder subject) : base(subject) 39 | { 40 | } 41 | 42 | /// 43 | /// Asserts that the specified module type has been registered on the current . 44 | /// 45 | /// The module type 46 | public void RegisterModule() where TModule : Module, new() 47 | { 48 | RegisterModule(typeof(TModule)); 49 | } 50 | 51 | /// 52 | /// Asserts that the specified module type has been registered on the current . 53 | /// 54 | /// The module type 55 | public void RegisterModule(Type moduleType) 56 | { 57 | EnsureVisited(); 58 | var module = _modules.FirstOrDefault(m => m.GetType() == moduleType); 59 | Execute.Assertion 60 | .ForCondition(module != null) 61 | .FailWith($"Module '{moduleType}' should be registered but it was not."); 62 | } 63 | 64 | /// 65 | /// Asserts that the modules contained in the specified assembly have been registered on the current 66 | /// . 67 | /// 68 | /// The module assembly 69 | /// More assemblies to assert 70 | public void RegisterAssemblyModules(Assembly assembly, params Assembly[] assemblies) 71 | { 72 | Enumerable.Repeat(assembly, 1) 73 | .Concat(assemblies) 74 | .ToList() 75 | .ForEach(RegisterModulesOf); 76 | } 77 | 78 | #region Private 79 | 80 | private void RegisterModulesOf(Assembly assembly) 81 | { 82 | var moduleTypes = assembly.GetTypes().Where(t => typeof(Module).IsAssignableFrom(t)).ToList(); 83 | moduleTypes.ForEach(RegisterModule); 84 | } 85 | 86 | private readonly List _modules = new(); 87 | private bool _visited; 88 | 89 | private void EnsureVisited() 90 | { 91 | if (_visited) 92 | return; 93 | 94 | // we will catch all directly registered modules here 95 | Visit(Subject); 96 | 97 | // modules loaded indirectly will be registered on the module-builder, 98 | // loop until no new modules discovered 99 | var modulesVisited = 0; 100 | while (_modules.Count > modulesVisited) 101 | { 102 | Visit(_moduleBuilder); 103 | modulesVisited = _modules.Count; 104 | } 105 | 106 | _visited = true; 107 | } 108 | 109 | private void Visit(ContainerBuilder builder) 110 | { 111 | CallbacksOf(builder).ToList().ForEach(Visit); 112 | } 113 | 114 | private void Visit(DeferredCallback callback) 115 | { 116 | Visit(callback.Callback); 117 | } 118 | 119 | private void Visit(Action callback) 120 | { 121 | switch (callback.Target) 122 | { 123 | case Module module: 124 | if (!_modules.Contains(module)) 125 | { 126 | _modules.Add(module); 127 | Load(module, _moduleBuilder); 128 | } 129 | 130 | return; 131 | case IModuleRegistrar registrar: 132 | CallbacksOf(registrar).ForEach(Visit); 133 | return; 134 | } 135 | 136 | _modules.AddRange(FieldsOf(callback.Target)); 137 | CallbacksOf(callback.Target).ForEach(Visit); 138 | } 139 | 140 | private static IEnumerable CallbacksOf(ContainerBuilder builder) 141 | { 142 | return FieldsOf>(builder) 143 | .SelectMany(list => list) 144 | .ToList(); 145 | } 146 | 147 | private static List> CallbacksOf(object value) 148 | { 149 | return FieldsOf>(value).ToList(); 150 | } 151 | 152 | private readonly ContainerBuilder _moduleBuilder = new(); 153 | 154 | private static readonly MethodInfo LoadModule = 155 | typeof(Module).GetMethod("Load", BindingFlags.NonPublic | BindingFlags.Instance); 156 | 157 | private static void Load(Module module, ContainerBuilder builder) 158 | { 159 | LoadModule.Invoke(module, new object[] { builder }); 160 | } 161 | 162 | private static IEnumerable FieldsOf(object value) 163 | { 164 | return value?.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) 165 | .Select(f => f.GetValue(value)) 166 | .OfType(); 167 | } 168 | 169 | #endregion 170 | } 171 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/ContainerAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Autofac; 7 | using Autofac.Util; 8 | using FluentAssertions.Primitives; 9 | 10 | namespace FluentAssertions.Autofac; 11 | 12 | /// 13 | /// 14 | /// Contains a number of methods to assert that an is in the expected state. 15 | /// 16 | #if !DEBUG 17 | [System.Diagnostics.DebuggerNonUserCode] 18 | #endif 19 | public class ContainerAssertions : ReferenceTypeAssertions 20 | { 21 | /// 22 | /// 23 | /// Returns the type of the subject the assertion applies on. 24 | /// 25 | [ExcludeFromCodeCoverage] 26 | protected override string Identifier => nameof(IComponentContext); 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// The subject 32 | public ContainerAssertions(IComponentContext container) : base(container) 33 | { 34 | } 35 | 36 | /// 37 | /// Returns an object that can be used to assert the current 38 | /// . 39 | /// 40 | public ContainerRegistrationAssertions Have() 41 | { 42 | return new ContainerRegistrationAssertions(Subject); 43 | } 44 | 45 | /// 46 | /// Returns an object that can be used to assert the current 47 | /// . 48 | /// 49 | public ResolveAssertions Resolve(Type serviceType) 50 | { 51 | return new ResolveAssertions(Subject, serviceType); 52 | } 53 | 54 | /// 55 | /// Returns an object that can be used to assert the current 56 | /// . 57 | /// 58 | public ResolveAssertions Resolve() 59 | { 60 | return Resolve(typeof(TService)); 61 | } 62 | 63 | /// 64 | /// Asserts the specified type has been registered with auto activation on the current 65 | /// . 66 | /// 67 | public void AutoActivate(Type type) 68 | { 69 | Subject.AssertAutoActivates(type); 70 | } 71 | 72 | /// 73 | /// Asserts the specified type has been registered with auto activation on the current 74 | /// . 75 | /// 76 | public void AutoActivate() 77 | { 78 | AutoActivate(typeof(TService)); 79 | } 80 | 81 | /// 82 | /// Returns an object that can be used to assert registered types on the current 83 | /// . 84 | /// 85 | /// 86 | public TypeScanningAssertions RegisterAssemblyTypes(params Assembly[] assemblies) 87 | { 88 | var types = assemblies.SelectMany(assembly => assembly.GetLoadableTypes()); 89 | return new TypeScanningAssertions(Subject, types); 90 | } 91 | 92 | /// 93 | /// Returns an object that can be used to assert registered types on the current 94 | /// . 95 | /// 96 | /// 97 | public TypeScanningAssertions RegisterTypes(IEnumerable types) 98 | { 99 | return new TypeScanningAssertions(Subject, types); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/ContainerRegistrationAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using FluentAssertions.Primitives; 5 | 6 | namespace FluentAssertions.Autofac; 7 | 8 | /// 9 | /// 10 | /// Contains a number of methods to assert that an has registered expected 11 | /// services. 12 | /// 13 | #if !DEBUG 14 | [System.Diagnostics.DebuggerNonUserCode] 15 | #endif 16 | public class 17 | ContainerRegistrationAssertions : ReferenceTypeAssertions 18 | { 19 | /// 20 | /// 21 | /// Returns the type of the subject the assertion applies on. 22 | /// 23 | [ExcludeFromCodeCoverage] 24 | protected override string Identifier => nameof(IComponentContext); 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The subject 30 | public ContainerRegistrationAssertions(IComponentContext subject) : base(subject) 31 | { 32 | } 33 | 34 | /// 35 | /// Returns an object that can be used to assert the current 36 | /// and . 37 | /// 38 | public RegisterAssertions Registered() 39 | { 40 | return new RegisterAssertions(Subject, typeof(TService)); 41 | } 42 | 43 | /// 44 | /// Returns an object that can be used to assert the current 45 | /// and the specified type. 46 | /// 47 | public RegisterAssertions Registered(Type type) 48 | { 49 | return new RegisterAssertions(Subject, type); 50 | } 51 | 52 | /// 53 | /// Returns an object that can be used to assert the current 54 | /// and the specified instance. 55 | /// 56 | public RegisterAssertions Registered(object instance) 57 | { 58 | if (instance == null) 59 | throw new ArgumentNullException(nameof(instance)); 60 | 61 | return new RegisterAssertions(Subject, instance.GetType()); 62 | } 63 | 64 | /// 65 | /// Asserts that the specified has not been registered on the current 66 | /// . 67 | /// 68 | /// The service type 69 | public void NotRegistered() 70 | { 71 | NotRegistered(typeof(TService)); 72 | } 73 | 74 | /// 75 | /// Asserts that the specified service type has not been registered on the current . 76 | /// 77 | /// The service type 78 | public void NotRegistered(Type type) 79 | { 80 | Subject.IsRegistered(type).Should().BeFalse($"Type '{type}' should not be registered"); 81 | } 82 | 83 | /// 84 | /// Asserts that the specified has not been registered on the current 85 | /// with the specified name. 86 | /// 87 | /// The service name 88 | /// The service type 89 | public void NotRegistered(string serviceName) 90 | { 91 | NotRegistered(serviceName, typeof(TService)); 92 | } 93 | 94 | /// 95 | /// Asserts that the specified service type has not been registered on the current 96 | /// with the 97 | /// specified name. 98 | /// 99 | /// The service name 100 | /// The service type 101 | public void NotRegistered(string serviceName, Type type) 102 | { 103 | Subject.IsRegisteredWithName(serviceName, type).Should() 104 | .BeFalse($"Type '{type}' should not be registered with name '{serviceName}'"); 105 | } 106 | 107 | /// 108 | /// Asserts that the specified service type has not been registered on the current 109 | /// with the 110 | /// specified key. 111 | /// 112 | /// The service key 113 | /// The service type 114 | public void NotRegistered(object serviceKey) 115 | { 116 | NotRegistered(serviceKey, typeof(TService)); 117 | } 118 | 119 | /// 120 | /// Asserts that the specified service type has not been registered on the current 121 | /// with the 122 | /// specified key. 123 | /// 124 | /// The service key 125 | /// The service type 126 | public void NotRegistered(object serviceKey, Type type) 127 | { 128 | Subject.IsRegisteredWithKey(serviceKey, type).Should() 129 | .BeFalse($"Type '{type}' should not be registered with key '{serviceKey}'"); 130 | } 131 | 132 | /// 133 | /// Returns an object that can be used to assert the current 134 | /// and the specified generic type. 135 | /// 136 | public RegisterGenericSourceAssertions RegisteredGeneric(Type genericComponentTypeDefinition) 137 | { 138 | return new RegisterGenericSourceAssertions(Subject, genericComponentTypeDefinition); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/FluentAssertions.Autofac.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | ..\ 4 | 5 | 6 | 7 | 8 | 9 | net6.0;net5.0;netcoreapp3.1;net48;net472;netstandard2.0;netstandard2.1 10 | true 11 | 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/Module.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | 4 | namespace FluentAssertions.Autofac; 5 | 6 | /// 7 | /// Contains extension methods for Module assertions. 8 | /// 9 | /// 10 | #if !DEBUG 11 | [System.Diagnostics.DebuggerNonUserCode] 12 | #endif 13 | public static class Module where TModule : Module, new() 14 | { 15 | /// 16 | /// Returns a test that can be used to assert the specified . 17 | /// 18 | /// optional builder arrangement for the module 19 | public static IContainer GetTestContainer(Action arrange = null) 20 | { 21 | return new TModule().Container(arrange); 22 | } 23 | 24 | /// 25 | /// Returns a test that can be used to assert the specified . 26 | /// 27 | /// optional builder arrangement for the module 28 | public static IContainer GetTestContainer(Action arrange) 29 | { 30 | return new TModule().Container(arrange); 31 | } 32 | 33 | /// 34 | /// Returns a test that can be used to assert the specified 35 | /// . 36 | /// 37 | /// optional builder arrangement for the module 38 | public static ContainerBuilder GetTestBuilder(Action arrange = null) 39 | { 40 | return new TModule().BuilderFor(arrange); 41 | } 42 | 43 | /// 44 | /// Returns a test that can be used to assert the specified 45 | /// . 46 | /// 47 | /// optional builder arrangement for the module 48 | public static ContainerBuilder GetTestBuilder(Action arrange) 49 | { 50 | return new TModule().BuilderFor(arrange); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/Package.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FluentAssertions.Autofac 5 | $Version$ 6 | FluentAssertions.Autofac 7 | Awesome Incremented and Contributors 8 | Awesome Incremented and Contributors 9 | http://www.opensource.org/licenses/mit-license.php 10 | false 11 | Fluent Assertions extensions for Autofac 12 | Fluent Assertions extensions for Autofac 13 | 14 | Revision: $Revision$ 15 | https://github.com/awesome-inc/FluentAssertions.Autofac 16 | 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/RegisterAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Autofac; 6 | 7 | namespace FluentAssertions.Autofac; 8 | 9 | /// 10 | /// Contains a number of methods to assert that an is in the expected state. 11 | /// 12 | #if !DEBUG 13 | [System.Diagnostics.DebuggerNonUserCode] 14 | #endif 15 | public class RegisterAssertions : RegistrationAssertions 16 | { 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The container 21 | /// The type that should be registered on the container 22 | public RegisterAssertions(IComponentContext subject, Type type) : base(subject, type) 23 | { 24 | } 25 | 26 | /// 27 | /// Asserts that the specified implementation type can be resolved from the current . 28 | /// 29 | /// The type to resolve 30 | public RegisterAssertions As() 31 | { 32 | var instances = Subject.Resolve>().ToArray(); 33 | var resolved = instances.FirstOrDefault(instance => instance.GetType() == Type); 34 | resolved.Should().NotBeNull($"Type '{Type}' should be registered as '{typeof(TResolve)}'"); 35 | return this; 36 | } 37 | 38 | /// 39 | /// Asserts that the specified implementation type can be resolved from the current . 40 | /// 41 | /// The type to resolve 42 | /// Optional types to resolve 43 | public RegisterAssertions As(Type type, params Type[] types) 44 | { 45 | AssertResolveAs(type); 46 | types?.ToList().ForEach(AssertResolveAs); 47 | return this; 48 | } 49 | 50 | /// 51 | /// Asserts that the registered service type can be resolved from the current . 52 | /// 53 | public RegisterAssertions AsSelf() 54 | { 55 | return As(Type); 56 | } 57 | 58 | /// 59 | /// Asserts that all implemented interfaces of the registered service type can be resolved from the current 60 | /// . 61 | /// 62 | public RegisterAssertions AsImplementedInterfaces() 63 | { 64 | GetImplementedInterfaces(Type).ForEach(AssertResolveAs); 65 | return this; 66 | } 67 | 68 | private void AssertResolveAs(Type serviceType) 69 | { 70 | new ResolveAssertions(Subject, serviceType).As(Type); 71 | } 72 | 73 | private static List GetImplementedInterfaces(Type type) 74 | { 75 | var interfaces = type.GetTypeInfo().ImplementedInterfaces 76 | .Where(i => i != typeof(IDisposable)) 77 | .ToList(); 78 | if (type.GetTypeInfo().IsInterface) 79 | interfaces.Add(type); 80 | return interfaces; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/RegisterGenericSourceAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using Autofac; 6 | using Autofac.Core; 7 | using Autofac.Core.Resolving.Pipeline; 8 | using FluentAssertions.Primitives; 9 | 10 | namespace FluentAssertions.Autofac; 11 | 12 | /// 13 | /// 14 | /// Contains a number of methods to assert that an is in the expected state. 15 | /// 16 | #if !DEBUG 17 | [System.Diagnostics.DebuggerNonUserCode] 18 | #endif 19 | public class 20 | RegisterGenericSourceAssertions : ReferenceTypeAssertions 21 | { 22 | private readonly Type _type; 23 | 24 | /// 25 | /// 26 | /// Returns the type of the subject the assertion applies on. 27 | /// 28 | [ExcludeFromCodeCoverage] 29 | protected override string Identifier => nameof(IComponentContext); 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// The component context 35 | /// The type that should be registered on the container 36 | public RegisterGenericSourceAssertions(IComponentContext subject, Type type) : 37 | base(subject) 38 | { 39 | AssertGenericType(type); 40 | _type = type; 41 | } 42 | 43 | /// 44 | /// Asserts that the specified service type can be resolved from the current . 45 | /// 46 | /// The type to resolve 47 | public RegistrationAssertions As(Type type) 48 | { 49 | var serviceType = GenericServiceTypeFor(type, out var componentType); 50 | var service = new TypedService(serviceType); 51 | var registration = RegistrationFor(type, service, componentType); 52 | return new RegistrationAssertions(Subject, registration); 53 | } 54 | 55 | /// 56 | /// Asserts that the specified service type can be resolved from the current . 57 | /// 58 | /// The service name 59 | /// The type to resolve 60 | public RegistrationAssertions Named(string serviceName, Type type) 61 | { 62 | var serviceType = GenericServiceTypeFor(type, out var componentType); 63 | var service = new KeyedService(serviceName, serviceType); 64 | var registration = RegistrationFor(type, service, componentType); 65 | return new RegistrationAssertions(Subject, registration); 66 | } 67 | 68 | private IComponentRegistration RegistrationFor(Type type, Service service, Type componentType) 69 | { 70 | var registration = RegistrationsFor(service).FirstOrDefault(); 71 | registration.Should() 72 | .NotBeNull($"there should be a registration source providing registrations for service {type.FullName}"); 73 | registration?.Activator.LimitType.Should() 74 | .Be(componentType, $"the generic component type definition registered should be {_type.FullName}."); 75 | return registration; 76 | } 77 | 78 | private Type GenericServiceTypeFor(Type type, out Type componentType) 79 | { 80 | AssertGenericType(type); 81 | var componentServicePairText = 82 | $"Component={_type.FullName} Service={type.FullName}"; 83 | 84 | _type.GetGenericArguments().Should().HaveCount(type.GetGenericArguments().Length, 85 | $"the generic arguments count of both generic component and generic service must be equal. {componentServicePairText}."); 86 | 87 | var argumentTypes = Enumerable.Repeat(typeof(object), 88 | _type.GetGenericArguments().Length).ToArray(); 89 | componentType = _type.MakeGenericType(argumentTypes); 90 | var serviceType = type.MakeGenericType(argumentTypes); 91 | 92 | componentType.Should().Implement(serviceType, 93 | $"component must implement specified service. {componentServicePairText}."); 94 | return serviceType; 95 | } 96 | 97 | private IEnumerable RegistrationsFor(Service service) 98 | { 99 | return Subject.ComponentRegistry.Sources 100 | .SelectMany(sources => sources.RegistrationsFor(service, Accessor) 101 | .ToList()); 102 | } 103 | 104 | private IEnumerable Accessor(Service service) 105 | { 106 | return Subject.ComponentRegistry.RegistrationsFor(service) 107 | .Select(c => new ServiceRegistration(ServicePipelines.DefaultServicePipeline, c)); 108 | } 109 | 110 | private static void AssertGenericType(Type type) 111 | { 112 | if (type == null) 113 | throw new ArgumentNullException(nameof(type)); 114 | 115 | if (!type.IsGenericTypeDefinition) 116 | { 117 | throw new ArgumentException("Type must be a generic type definition.", 118 | nameof(type)); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/RegistrationAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Autofac; 7 | using Autofac.Core; 8 | using Autofac.Core.Activators.Reflection; 9 | using Autofac.Core.Lifetime; 10 | using FluentAssertions.Primitives; 11 | 12 | namespace FluentAssertions.Autofac; 13 | 14 | /// 15 | /// 16 | /// Contains a number of methods to assert that an registers value services. 17 | /// 18 | #if !DEBUG 19 | [System.Diagnostics.DebuggerNonUserCode] 20 | #endif 21 | public class RegistrationAssertions : ReferenceTypeAssertions 22 | { 23 | /// 24 | /// The type that should be registered on the container 25 | /// 26 | internal readonly Type Type; 27 | 28 | private readonly IComponentRegistration _registration; 29 | private readonly IList _parameters; 30 | 31 | /// 32 | /// 33 | /// Returns the type of the subject the assertion applies on. 34 | /// 35 | [ExcludeFromCodeCoverage] 36 | protected override string Identifier => nameof(IComponentContext); 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// The container 42 | /// The type that should be registered on the container 43 | public RegistrationAssertions(IComponentContext subject, Type type) : base(subject) 44 | { 45 | Type = type ?? throw new ArgumentNullException(nameof(type)); 46 | _registration = Subject.ComponentRegistry.GetRegistration(Type); 47 | _parameters = GetParameters(_registration); 48 | } 49 | 50 | /// 51 | /// Initializes a new instance of the class. 52 | /// 53 | /// The container 54 | /// 55 | public RegistrationAssertions(IComponentContext subject, IComponentRegistration registration) : base(subject) 56 | { 57 | _registration = registration ?? throw new ArgumentNullException(nameof(registration)); 58 | Type = registration.Activator.LimitType; 59 | _parameters = GetParameters(_registration); 60 | } 61 | 62 | /// 63 | /// Asserts that the specified has been registered on the current 64 | /// with the specified name. 65 | /// 66 | /// The service name 67 | /// The service type 68 | public RegistrationAssertions Named(string name) 69 | { 70 | return Named(name, typeof(TService)); 71 | } 72 | 73 | /// 74 | /// Asserts that the specified has been registered on the current 75 | /// 76 | /// with the specified name. 77 | /// 78 | /// The service name 79 | /// The service type 80 | public RegistrationAssertions Named(string name, Type type) 81 | { 82 | Subject.IsRegisteredWithName(name, type) 83 | .Should().BeTrue($"Type '{type}' should be registered with name '{name}'"); 84 | return this; 85 | } 86 | 87 | /// 88 | /// Asserts that the specified has been registered on the current 89 | /// with the specified key. 90 | /// 91 | /// The service key 92 | /// The service type 93 | public RegistrationAssertions Keyed(object key) 94 | { 95 | return Keyed(key, typeof(TService)); 96 | } 97 | 98 | /// 99 | /// Asserts that the specified has been registered on the current 100 | /// with the specified key. 101 | /// 102 | /// The service key 103 | /// The service type 104 | public RegistrationAssertions Keyed(object key, Type type) 105 | { 106 | Subject.IsRegisteredWithKey(key, type) 107 | .Should().BeTrue($"Type '{type}' should be registered with key '{key}'"); 108 | return this; 109 | } 110 | 111 | /// 112 | /// Asserts that the current service type has been registered as singleton on the current 113 | /// . 114 | /// 115 | public RegistrationAssertions SingleInstance() 116 | { 117 | return Lifetime() 118 | .Shared(InstanceSharing.Shared); 119 | } 120 | 121 | /// 122 | /// Asserts that the current service type has been registered as 'instance per dependency' on the current 123 | /// . 124 | /// 125 | public RegistrationAssertions InstancePerDependency() 126 | { 127 | return Lifetime() 128 | .Shared(InstanceSharing.None); 129 | } 130 | 131 | /// 132 | /// Asserts that the current service type has been registered as 'instance per lifetime scope' on the current 133 | /// . 134 | /// 135 | public RegistrationAssertions InstancePerLifetimeScope() 136 | { 137 | return Lifetime() 138 | .Shared(InstanceSharing.Shared); 139 | } 140 | 141 | /// 142 | /// Asserts that the current service type has been registered as 'instance per matching lifetime scope' on the current 143 | /// . 144 | /// 145 | public RegistrationAssertions InstancePerMatchingLifetimeScope() 146 | { 147 | return Lifetime() 148 | .Shared(InstanceSharing.Shared); 149 | } 150 | 151 | /// 152 | /// Asserts that the current service type has been registered as 'instance per request' on the current 153 | /// . 154 | /// 155 | public RegistrationAssertions InstancePerRequest() 156 | { 157 | return Lifetime() 158 | .Shared(InstanceSharing.Shared); 159 | } 160 | 161 | /// 162 | /// Asserts that the current service type has been registered as 'instance per owned' of the specified 163 | /// on the current . 164 | /// 165 | public RegistrationAssertions InstancePerOwned() 166 | { 167 | return InstancePerOwned(typeof(TService)); 168 | } 169 | 170 | /// 171 | /// Asserts that the current service type has been registered as 'instance per owned' of the specified 172 | /// on the current . 173 | /// 174 | public RegistrationAssertions InstancePerOwned(Type serviceType) 175 | { 176 | // TODO 177 | return InstancePerMatchingLifetimeScope(); 178 | } 179 | 180 | /// 181 | /// Asserts that the current service type has been registered as 'externally owned' on the current 182 | /// . 183 | /// 184 | public RegistrationAssertions ExternallyOwned() 185 | { 186 | return Owned(InstanceOwnership.ExternallyOwned); 187 | } 188 | 189 | /// 190 | /// Asserts that the current service type has been registered as 'owned by lifetime scope' on the current 191 | /// . 192 | /// 193 | public RegistrationAssertions OwnedByLifetimeScope() 194 | { 195 | return Owned(InstanceOwnership.OwnedByLifetimeScope); 196 | } 197 | 198 | /// 199 | /// Asserts the current service type has been registered using the specified on the 200 | /// current . 201 | /// 202 | /// An optional custom assertion action to execute on the 203 | /// 204 | public RegistrationAssertions Lifetime(Action assert = null) 205 | where TLifetime : IComponentLifetime 206 | { 207 | _registration.Lifetime.Should() 208 | .BeOfType($"Type '{Type}' should be registered with lifetime '{typeof(TLifetime)}'"); 209 | assert?.Invoke((TLifetime)_registration.Lifetime); 210 | return this; 211 | } 212 | 213 | /// 214 | /// Asserts the current service type has been registered using the specified on the 215 | /// current . 216 | /// 217 | /// The instance sharing mode 218 | public RegistrationAssertions Shared(InstanceSharing sharing) 219 | { 220 | _registration.Sharing.Should().Be(sharing, $"Type '{Type}' should be shared as '{sharing}'"); 221 | return this; 222 | } 223 | 224 | /// 225 | /// Asserts the current service type has been registered using the specified on the 226 | /// current . 227 | /// 228 | /// The instance ownership mode 229 | public RegistrationAssertions Owned(InstanceOwnership ownership) 230 | { 231 | _registration.Ownership.Should().Be(ownership, $"Type '{Type}' should be owned '{ownership}'"); 232 | return this; 233 | } 234 | 235 | /// 236 | /// Asserts the current service type has been registered with auto activation on the current 237 | /// . 238 | /// 239 | public RegistrationAssertions AutoActivate() 240 | { 241 | _registration.AssertAutoActivates(Type); 242 | return this; 243 | } 244 | 245 | /// 246 | /// Asserts the current service type has been registered with the specified constructor parameter. 247 | /// 248 | /// The parameter name 249 | /// The parameter value 250 | public RegistrationAssertions WithParameter(string name, object value) 251 | { 252 | return WithParameter(new NamedParameter(name, value)); 253 | } 254 | 255 | /// 256 | /// Asserts the current service type has been registered with the specified constructor parameter. 257 | /// 258 | /// 259 | /// Must evaluate to true for a parameter for the assertion to pass. 260 | /// 261 | /// 262 | /// When null, assertion passes when one or more of the parameters matches the 263 | /// . When set to a value, exactly this number of parameters must match the 264 | /// . 265 | /// 266 | /// 267 | public RegistrationAssertions WithParameter( 268 | Func predicate, 269 | int? matchCount = null) 270 | { 271 | var matchingParams = _parameters.Where(predicate); 272 | 273 | if (matchCount.HasValue) 274 | { 275 | matchingParams 276 | .Count() 277 | .Should() 278 | .Be( 279 | matchCount.Value, 280 | $"exactly {matchCount.Value} parameter(s) matching a predicate should have been registered"); 281 | } 282 | else 283 | { 284 | matchingParams 285 | .Any() 286 | .Should() 287 | .BeTrue("at least one parameter matching a predicate should have been registered"); 288 | } 289 | 290 | return this; 291 | } 292 | 293 | /// 294 | /// Asserts the current service type has been registered with the specified constructor parameter. 295 | /// 296 | /// The parameter 297 | public RegistrationAssertions WithParameter(NamedParameter param) 298 | { 299 | var p = _parameters 300 | .OfType() 301 | .FirstOrDefault(np => np.Name == param.Name); 302 | 303 | p.Should().NotBeNull($"Parameter '{param.Name}' should have been registered."); 304 | p?.Value.Should().BeEquivalentTo(param.Value, 305 | $"Parameter '{param.Name}' should have been registered with value '{param.Value}'."); 306 | 307 | return this; 308 | } 309 | 310 | /// 311 | /// Asserts the current service type has been registered with the specified constructor parameter. 312 | /// 313 | /// The parameter 314 | public RegistrationAssertions WithParameter(PositionalParameter param) 315 | { 316 | var p = _parameters 317 | .OfType() 318 | .FirstOrDefault(pp => pp.Position == param.Position); 319 | 320 | p.Should().NotBeNull($"Parameter should have been registered at position '{param.Position}'."); 321 | p?.Value.Should().BeEquivalentTo(param.Value, 322 | $"Parameter at position '{param.Position}' should have been registered with value '{param.Value}'."); 323 | 324 | return this; 325 | } 326 | 327 | private static IList GetParameters(IComponentRegistration registration) 328 | { 329 | var parameters = new List(); 330 | 331 | if (!(registration.Activator is ReflectionActivator activator)) 332 | { 333 | return parameters; 334 | } 335 | 336 | const string fieldName = "_defaultParameters"; 337 | const BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | 338 | BindingFlags.Static; 339 | var field = activator.GetType().GetField(fieldName, bindFlags); 340 | if (field == null) 341 | { 342 | return parameters; 343 | } 344 | 345 | if (field.GetValue(activator) is Parameter[] p) 346 | { 347 | parameters.AddRange(p); 348 | } 349 | 350 | return parameters; 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/ResolveAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using Autofac; 6 | using FluentAssertions.Execution; 7 | using FluentAssertions.Primitives; 8 | 9 | namespace FluentAssertions.Autofac; 10 | 11 | /// 12 | /// 13 | /// Contains a number of methods to assert that expected services can actually be resolved from an 14 | /// . 15 | /// 16 | #if !DEBUG 17 | [System.Diagnostics.DebuggerNonUserCode] 18 | #endif 19 | public class ResolveAssertions : ReferenceTypeAssertions 20 | { 21 | private readonly Type _serviceType; 22 | private readonly List _instances = new(); 23 | 24 | /// 25 | /// 26 | /// Returns the type of the subject the assertion applies on. 27 | /// 28 | [ExcludeFromCodeCoverage] 29 | protected override string Identifier => nameof(IComponentContext); 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// The container 35 | /// The service type 36 | public ResolveAssertions(IComponentContext container, Type serviceType) : base(container) 37 | { 38 | _serviceType = serviceType; 39 | var typeToResolve = typeof(IEnumerable<>).MakeGenericType(serviceType); 40 | if (Subject.Resolve(typeToResolve) is Array array) 41 | { 42 | _instances.AddRange(array.OfType()); 43 | } 44 | 45 | Execute.Assertion 46 | .ForCondition(_instances.Any()) 47 | .FailWith($"Expected container to resolve '{_serviceType}' but it did not."); 48 | } 49 | 50 | /// 51 | /// Asserts that the specified implementation type can be resolved from the current . 52 | /// 53 | /// The type to resolve 54 | // ReSharper disable once UnusedMember.Global 55 | public RegistrationAssertions As() 56 | { 57 | return As(typeof(TImplementation)); 58 | } 59 | 60 | /// 61 | /// Asserts that the specified implementation type(s) can be resolved from the current . 62 | /// 63 | /// The type to resolve 64 | /// Optional types to resolve 65 | public RegistrationAssertions As(Type type, params Type[] types) 66 | { 67 | AssertTypeResolved(type); 68 | types.ToList().ForEach(AssertTypeResolved); 69 | return new RegistrationAssertions(Subject, type); 70 | } 71 | 72 | /// 73 | /// Asserts that the registered service type can be resolved from the current . 74 | /// 75 | public RegistrationAssertions AsSelf() 76 | { 77 | return As(_serviceType); 78 | } 79 | 80 | 81 | /// 82 | /// Asserts that the service type has been registered with auto activation on the current 83 | /// . 84 | /// 85 | public void AutoActivate() 86 | { 87 | Subject.AssertAutoActivates(_serviceType); 88 | } 89 | 90 | private void AssertTypeResolved(Type type) 91 | { 92 | _instances.Should().Contain(instance => instance.GetType() == type, 93 | $"Type '{_serviceType}' should be resolved as '{type}'"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/TestExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Autofac; 4 | 5 | namespace FluentAssertions.Autofac; 6 | 7 | /// 8 | /// Contains extension methods for Autofac Containers and Builder assertions. 9 | /// 10 | [DebuggerNonUserCode] 11 | public static class TestExtensions 12 | { 13 | /// 14 | /// Returns an suitable for testing the specified module. 15 | /// 16 | /// The module 17 | /// optional builder arrangement for the module 18 | public static IContainer Container(this TModule module, Action arrange = null) 19 | where TModule : Module 20 | { 21 | if (module == null) 22 | throw new ArgumentNullException(nameof(module)); 23 | 24 | var builder = BuilderFor(module, arrange); 25 | return builder.Build(); 26 | } 27 | 28 | /// 29 | /// Returns an suitable for testing the specified module. 30 | /// 31 | /// The module 32 | /// optional builder arrangement for the module 33 | public static ContainerBuilder BuilderFor(this TModule module, Action arrange = null) 34 | where TModule : Module 35 | { 36 | if (module == null) 37 | throw new ArgumentNullException(nameof(module)); 38 | 39 | var builder = new ContainerBuilder(); 40 | arrange?.Invoke(builder); 41 | builder.RegisterModule(module); 42 | return builder; 43 | } 44 | 45 | /// 46 | /// Returns an suitable for testing the specified module. 47 | /// 48 | /// The module 49 | /// optional builder arrangement for the module 50 | public static IContainer Container(this TModule module, Action arrange) 51 | where TModule : Module 52 | { 53 | if (module == null) 54 | throw new ArgumentNullException(nameof(module)); 55 | 56 | if (arrange == null) 57 | throw new ArgumentNullException(nameof(arrange)); 58 | 59 | var builder = BuilderFor(module, arrange); 60 | return builder.Build(); 61 | } 62 | 63 | /// 64 | /// Returns an suitable for testing the specified module. 65 | /// 66 | /// The module 67 | /// optional builder arrangement for the module 68 | public static ContainerBuilder BuilderFor(this TModule module, Action arrange) 69 | where TModule : Module 70 | { 71 | if (module == null) 72 | throw new ArgumentNullException(nameof(module)); 73 | 74 | if (arrange == null) 75 | throw new ArgumentNullException(nameof(arrange)); 76 | 77 | var builder = new ContainerBuilder(); 78 | arrange.Invoke(builder, module); 79 | builder.RegisterModule(module); 80 | return builder; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/TypeScanningAssertions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using Autofac; 8 | using FluentAssertions.Primitives; 9 | 10 | namespace FluentAssertions.Autofac; 11 | 12 | /// 13 | /// 14 | /// Contains a number of methods to assert registered types on an . 15 | /// 16 | #if !DEBUG 17 | [System.Diagnostics.DebuggerNonUserCode] 18 | #endif 19 | public class TypeScanningAssertions : ReferenceTypeAssertions 20 | { 21 | /// 22 | /// The types. 23 | /// 24 | public readonly IEnumerable Types; 25 | 26 | private readonly Lazy> _registerAssertions; 27 | 28 | private List Register => _registerAssertions.Value; 29 | 30 | /// 31 | /// 32 | /// Returns the type of the subject the assertion applies on. 33 | /// 34 | [ExcludeFromCodeCoverage] 35 | protected override string Identifier => nameof(IComponentContext); 36 | 37 | /// 38 | /// Initializes a new instance of the class. 39 | /// 40 | /// 41 | /// The types to assert 42 | /// 43 | public TypeScanningAssertions(IComponentContext subject, IEnumerable types) : base(subject) 44 | { 45 | Types = FilterTypes(types); 46 | _registerAssertions = new Lazy>( 47 | () => Types.Select(t => Subject.Should().Have().Registered(t)).ToList()); 48 | } 49 | 50 | /// 51 | /// Specifies a subset of types to register from a scanned assembly using 52 | /// the specified . 53 | /// 54 | public TypeScanningAssertions Where(Func predicate) 55 | { 56 | return new TypeScanningAssertions(Subject, Types.Where(predicate)); 57 | } 58 | 59 | /// 60 | /// Specifies a subset of types to register from a scanned assembly. 61 | /// 62 | public TypeScanningAssertions Except() 63 | { 64 | return Where(t => t != typeof(T)); 65 | } 66 | 67 | /// 68 | /// Asserts that the scanned types can be resolved from the current 69 | /// as the specified . 70 | /// 71 | public TypeScanningAssertions As() 72 | { 73 | return As(typeof(T)); 74 | } 75 | 76 | /// 77 | /// Asserts that the scanned types can be resolved from the current 78 | /// as the specified . 79 | /// 80 | public TypeScanningAssertions As(Type type) 81 | { 82 | Register.ForEach(r => r.As(type)); 83 | return this; 84 | } 85 | 86 | /// 87 | /// Asserts that the scanned types can be resolved from the current 88 | /// as the type returned using the specified lambda. 89 | /// 90 | public TypeScanningAssertions As(Func lambda) 91 | { 92 | Register.ForEach(r => r.As(lambda(r.Type))); 93 | return this; 94 | } 95 | 96 | /// 97 | /// Asserts that the scanned types can be resolved from the current as self. 98 | /// 99 | public TypeScanningAssertions AsSelf() 100 | { 101 | Register.ForEach(r => r.AsSelf()); 102 | return this; 103 | } 104 | 105 | /// 106 | /// Asserts that the scanned types can be resolved from the current 107 | /// as their implemented interfaces. 108 | /// 109 | public TypeScanningAssertions AsImplementedInterfaces() 110 | { 111 | Register.ForEach(r => r.AsImplementedInterfaces()); 112 | return this; 113 | } 114 | 115 | private static IEnumerable FilterTypes(IEnumerable types) 116 | { 117 | return types.Where(t => 118 | t.GetTypeInfo().IsClass && 119 | !t.GetTypeInfo().IsAbstract && 120 | !t.GetTypeInfo().IsGenericTypeDefinition && 121 | !IsDelegate(t) && 122 | !IsCompilerGenerated(t)); 123 | } 124 | 125 | private static bool IsDelegate(Type type) 126 | { 127 | return type.GetTypeInfo().IsSubclassOf(typeof(Delegate)); 128 | } 129 | 130 | private static bool IsCompilerGenerated(Type type) 131 | { 132 | return type.GetTypeInfo().GetCustomAttributes().Any(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /FluentAssertions.Autofac/TypeScanningAssertionsExtensions.cs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDelivery 2 | branches: { } 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FluentAssertions.Autofac 2 | 3 | [![Build status](https://github.com/fluentassertions/fluentassertions.autofac/actions/workflows/build.yml/badge.svg)](https://github.com/fluentassertions/fluentassertions.autofac/actions/workflows/build.yml) 4 | 5 | [![NuGet](https://img.shields.io/nuget/v/FluentAssertions.Autofac.svg?style=flat-square)](https://www.nuget.org/packages/FluentAssertions.Autofac/) 6 | [![NuGet](https://img.shields.io/nuget/dt/FluentAssertions.Autofac.svg?style=flat-square)](https://www.nuget.org/packages/FluentAssertions.Autofac/) 7 | 8 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=fluentassertions.FluentAssertions.Autofac&metric=alert_status)](https://sonarcloud.io/dashboard?id=fluentassertions.FluentAssertions.Autofac) 9 | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=fluentassertions.FluentAssertions.Autofac&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=fluentassertions.FluentAssertions.Autofac) 10 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=fluentassertions.FluentAssertions.Autofac&metric=coverage)](https://sonarcloud.io/summary/new_code?id=fluentassertions.FluentAssertions.Autofac) 11 | 12 | This repository contains the [Fluent Assertions](http://fluentassertions.com/) extensions 13 | for [Autofac](https://autofac.org/). It is maintained by [@mkoertgen](https://github.com/mkoertgen). 14 | 15 | - See [www.fluentassertions.com](http://www.fluentassertions.com/) for more information about the main library. 16 | 17 | ## Why? 18 | 19 | In general, the more you apply [Dependency Injection (DI)](http://martinfowler.com/articles/injection.html) the easier 20 | becomes unit testing and [Test-driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development). 21 | 22 | This is because the complexity of constructing all dependencies is shifted to the so 23 | called [Composition Root](http://blog.ploeh.dk/2011/07/28/CompositionRoot/), i.e. the place where you "wire up" and 24 | configure all your dependencies. Undoubtedly, the best way to do this is by using 25 | some [Inversion of Control (IoC)](http://martinfowler.com/articles/injection.html) container. 26 | 27 | With an application growing in complexity, there is also growing need to organize and test the IoC configuration. 28 | 29 | ## Quickstart 30 | 31 | ### Usage 32 | 33 | Install the NuGet package [FluentAssertions.Autofac](https://www.nuget.org/packages/FluentAssertions.Autofac/) and start writing tests for your Autofac configuration. 34 | 35 | ```csharp 36 | container.Should().Have().Registered() 37 | .AsSelf() 38 | .As() 39 | .Singleton(); 40 | ``` 41 | 42 | Find more examples in the [documentation](_docs/index.md) or the [tests](./FluentAssertions.Autofac.Tests/). 43 | 44 | ### How to build 45 | 46 | Clone and build using Rider, Code, Visual Studio, ... or the command line using [nuke.build](https://nuke.build/). 47 | 48 | ### Links 49 | 50 | - [Why?](_docs/why.md) 51 | - [Usage](_docs/usage.md) 52 | - [Contributing](https://github.com/fluentassertions/fluentAssertions.autofac/blob/master/CONTRIBUTING.md) 53 | -------------------------------------------------------------------------------- /SampleLib/DeviceState.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | // ReSharper disable once UnusedMember.Global 4 | public enum DeviceState { Online, Offline } 5 | -------------------------------------------------------------------------------- /SampleLib/IDeviceState.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | internal interface IDeviceState 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/INamedInstance.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | internal interface INamedInstance 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/ISampleInstance.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | public interface ISampleInstance 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/ISampleService.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | public interface ISampleService 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/NamedInstance.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | public class NamedInstance : INamedInstance 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/OnlineState.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | internal class OnlineState : IDeviceState 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/SampleLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | False 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SampleLib/SampleModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using NSubstitute; 3 | 4 | namespace SampleLib; 5 | 6 | public class SampleModule : Module 7 | { 8 | internal static readonly ISampleInstance SampleInstance = Substitute.For(); 9 | 10 | protected override void Load(ContainerBuilder builder) 11 | { 12 | builder.RegisterType().As(); 13 | 14 | builder.RegisterInstance(SampleInstance).SingleInstance(); 15 | 16 | builder.RegisterType().Named("SampleName"); 17 | builder.RegisterType().Keyed(DeviceState.Online); 18 | builder.RegisterType().AutoActivate(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SampleLib/SampleModule_Should.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using FluentAssertions.Autofac; 4 | using Xunit; 5 | 6 | namespace SampleLib; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class SampleModule_Should 10 | { 11 | private readonly IContainer _container = Module.GetTestContainer(); 12 | 13 | [Theory] 14 | [InlineData(typeof(SampleService), typeof(ISampleService))] 15 | public void Register(Type serviceType, Type contractType) 16 | { 17 | _container.Should().Have().Registered(serviceType).As(contractType); 18 | } 19 | 20 | [Fact] 21 | public void Register_SampleService() 22 | { 23 | _container.Should().Have().Registered(typeof(SampleService)).As(typeof(ISampleService)); 24 | _container.Should().Have().Registered().As(); 25 | } 26 | 27 | [Fact] 28 | public void Register_SampleInstance() 29 | { 30 | _container.Should().Have().Registered(SampleModule.SampleInstance) 31 | .As(); 32 | } 33 | 34 | [Theory] 35 | [InlineData(typeof(INamedInstance), "SampleName")] 36 | public void Register_Named(Type type, string name) 37 | { 38 | _container.Should().Have().Registered().Named(name); 39 | _container.Should().Have().Registered(typeof(NamedInstance)).Named(name, type); 40 | } 41 | 42 | [Theory] 43 | [InlineData(typeof(IDeviceState), DeviceState.Online)] 44 | public void Register_Keyed(Type type, DeviceState key) 45 | { 46 | _container.Should().Have().Registered().Keyed(key); 47 | _container.Should().Have().Registered(typeof(OnlineState)).Keyed(key, type); 48 | } 49 | 50 | [Fact] 51 | public void AutoActivate_SampleStarter() 52 | { 53 | _container.Should().Have().Registered().AutoActivate(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SampleLib/SampleService.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | internal class SampleService : ISampleService 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SampleLib/SampleStarter.cs: -------------------------------------------------------------------------------- 1 | namespace SampleLib; 2 | 3 | internal class SampleStarter 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /SolutionInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyDescription("FluentAssertions.Autofac")] 5 | [assembly: AssemblyProduct("FluentAssertions.Autofac")] 6 | 7 | #if DEBUG 8 | [assembly: AssemblyConfiguration("Debug")] 9 | #else 10 | [assembly: AssemblyConfiguration("Release")] 11 | #endif 12 | 13 | [assembly: AssemblyCompany("FluentAssertions")] 14 | [assembly: AssemblyCopyright("Copyright © FluentAssertions and Contributors 2020")] 15 | [assembly: AssemblyTrademark("FluentAssertions")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: ComVisible(false)] 18 | -------------------------------------------------------------------------------- /_docs/index.md: -------------------------------------------------------------------------------- 1 | # FluentAssertions.Autofac Docs 2 | 3 | Fluent Assertions extensions for Autofac. 4 | 5 | - Introductory Blog 6 | Post [Fluently testing your Autofac configuration](http://awesome-incremented.blogspot.de/2016/01/fluently-testing-your-autofac.html) 7 | - [Why](./why.md) 8 | - [Usage](./usage.md) 9 | -------------------------------------------------------------------------------- /_docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Available on [Nuget](https://www.nuget.org) 4 | under [FluentAssertions.Autofac](https://www.nuget.org/packages/FluentAssertions.Autofac/) 5 | 6 | ```powershell 7 | Install-Package FluentAssertions.Autofac 8 | ``` 9 | 10 | ## Test Registration 11 | 12 | Testing your DI configuration logic works by testing against your container: 13 | 14 | ```csharp 15 | var container = Configure(); 16 | container.Should().Have().Registered() 17 | .AsSelf() 18 | .As() 19 | .Singleton(); 20 | 21 | container.Should().Have().Registered(superCoolInstance); 22 | 23 | container.Should().Have().Registered() 24 | .Named("uncool"); 27 | ``` 28 | 29 | ## Test Resolving 30 | 31 | You can also test actually resolving an instance which verifies your registration is complete: 32 | 33 | ```csharp 34 | container.Should().Resolve().As() 35 | .AutoActivate(); 36 | ``` 37 | 38 | ## Testing Autofac Modules 39 | 40 | With an application growing in complexity you want to modularize your configuration logic 41 | with [Autofac Modules](http://autofac.readthedocs.org/en/latest/configuration/modules.html). Testing your modules is 42 | then achieved using the `Module` helper class which supports creating isolated & testable builder and 43 | containers. 44 | 45 | Here is a simple example verifying that the module `MyCoolModule` registers the `SuperCoolService` implementation 46 | 47 | ```csharp 48 | Module.GetTestContainer() 49 | .Should().Have().Registered() 50 | ... 51 | ``` 52 | 53 | You can also retreive a testable builder like this 54 | 55 | ```csharp 56 | Module.GetTestBuilder() 57 | .Should().RegisterModule() 58 | ... 59 | ``` 60 | 61 | In general, when implementing modules you will typically be subclassing Autofac's module base class `Module` which 62 | requires a parameterless constructor. And this is fine in most cases. 63 | 64 | However, we have had some more special use cases where we definitely wanted some module to take constructor parameters. 65 | In this case you cannot use the generic idiom 66 | 67 | ```csharp 68 | builder.RegisterModule(); 69 | ``` 70 | 71 | Instead you will use the instance variant 72 | 73 | ```csharp 74 | builder.RegisterModule(module); 75 | ``` 76 | 77 | The way to test this is then by using the `Container(...)` and `Builder(...)` extension methods 78 | 79 | ```csharp 80 | var sut = new MyModule(...); 81 | sut.Container(arrange: ..., types: ...) 82 | .Should... 83 | 84 | sut.Builder(arrange: ..., types: ...) 85 | ``` 86 | 87 | ### Mocking module dependencies 88 | 89 | In case your module depends on some cross cutting dependencies you need some extra arrangement for the testing container 90 | to work with your module. 91 | 92 | You can inject an action to arrange substitutes like this 93 | 94 | ```csharp 95 | var container = Module.GetTestContainer(builder => 96 | { 97 | builder.RegisterInstance(Substitute.For()); 98 | ... 99 | }); 100 | ``` 101 | 102 | ### Testing a module register some other modules 103 | 104 | Say one thing your module does is registering some other modules like this 105 | 106 | ```csharp 107 | builder.RegisterModule(); 108 | builder.RegisterAssemblyModules(typeof(MyType).Assembly); 109 | ``` 110 | 111 | This is a use case where you verify that some callbacks have been registered not on the container but on the builder 112 | instead 113 | 114 | ```csharp 115 | Module.GetTestBuilder().Should() 116 | .RegisterModule() 117 | .RegisterAssemblyModules(typeof(MyType).Assembly, ... more assemblies); 118 | ``` 119 | -------------------------------------------------------------------------------- /_docs/why.md: -------------------------------------------------------------------------------- 1 | # Why test your IoC configuration in the first place? 2 | 3 | In general, the more you apply [Dependency Injection (DI)](http://martinfowler.com/articles/injection.html) the easier 4 | becomes unit testing and [Test-driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development). 5 | 6 | This is because the complexity of constructing all dependencies is shifted to the so 7 | called [Composition Root](http://blog.ploeh.dk/2011/07/28/CompositionRoot/), i.e. the place where you "wire up" and 8 | configure all your dependencies. Undoubtedly, the best way to do this is by using 9 | some [Inversion of Control (IoC)](http://martinfowler.com/articles/injection.html) container. 10 | 11 | With an application growing in complexity, there is also growing need to organize and test the IoC configuration. 12 | 13 | **Here is the usual story:** During release sprints we typically develop features in parallel that form complete 14 | workflows (think [epics or themes](https://www.mountaingoatsoftware.com/blog/stories-epics-and-themes), if you will). 15 | While carving these out, we mock out dependencies to the other features. This way everyone can move forward without 16 | needing the other features completed. 17 | 18 | For example, say you're designing the coolest recommendation view on the planet. While your buddy is on the 19 | recommendation service on the backend, you will register a mock, i.e. 20 | 21 | ```csharp 22 | builder.RegisterType().As(); 23 | // builder.RegisterType().As(); 24 | ``` 25 | 26 | Once your buddy gives thumbs up for integration testing you switch to the real thing. Finally, you finish and move on to 27 | the next feature. 28 | 29 | So far, so good. But then, a **critical issue** with your view (or likewise viewmodel, controller) rises in production. 30 | No problem, you go for the hotfix, switch back to your mock so you can better and quicker reproduce the problem. You fix 31 | the problem **but then, you forget to switch back from the mock to the productive recommendation service** - which gives 32 | you a serious [#facepalm](https://en.wikipedia.org/wiki/Facepalm). 33 | 34 | ![Facepalm (Wikipedia)](https://upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Paris_Tuileries_Garden_Facepalm_statue.jpg/300px-Paris_Tuileries_Garden_Facepalm_statue.jpg) 35 | 36 | Wouldn't it be nice if there was a test saving you from breaking the release next time? Well, now there is. 37 | 38 | ## Fluent interfaces and readable code 39 | 40 | Most of the time, we prefer using this stack 41 | 42 | - [Autofac](http://autofac.org/) for wiring up DI, 43 | - [NSubstitute](http://nsubstitute.github.io/) for mocking and 44 | - [FluentAssertions](http://www.fluentassertions.com/) for extremely readable tests that naturally explain when failing. 45 | 46 | There is nothing too special about this choice. But there is at least one thing these libraries have in common: They all 47 | share a profound intent on improving accessibility and readability by providing 48 | a [fluent API](https://en.wikipedia.org/wiki/Fluent_interface). 49 | 50 | In the rare case that you - as a developer - never quoted that *"Code is read much more often than it is written"* and 51 | are wondering why you should thrive for readable code in the first place, then here is a hand-picked selection of 52 | classic references that we found to be helpful 53 | 54 | - [What Do Programmers Really Do Anyway? (Peter Hallam, 2006)](http://blogs.msdn.com/b/peterhal/archive/2006/01/04/509302.aspx) 55 | - [When Understanding means Rewriting (Jeff Atwood, 2006)](http://blog.codinghorror.com/when-understanding-means-rewriting/) 56 | - [Code is read much more often than it is written, so plan accordingly (R.Chen - MSFT, 2007)](https://blogs.msdn.microsoft.com/oldnewthing/20070406-00/?p=27343) 57 | - [Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin et al., 2008)](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) 58 | - [The Futility of Commenting Code (Bob Carpenter, 2009)](http://lingpipe-blog.com/2009/10/15/the-futility-of-commenting-code/) 59 | - [Learn to Read the Source, Luke (J.Atwood 2012)](http://blog.codinghorror.com/learn-to-read-the-source-luke/) 60 | - [Review of M.Fowlers classic "Refactoring: Improving the Design of Existing Code" (2012)](http://siderite.blogspot.com/2012/04/refactoring-improving-design-of.html) 61 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj" 17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "Current" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function ExecSafe([scriptblock] $cmd) { 32 | & $cmd 33 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 34 | } 35 | 36 | # If dotnet CLI is installed globally and it matches requested version, use for execution 37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 40 | } 41 | else { 42 | # Download install script 43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 47 | 48 | # If global.json exists, load expected version 49 | if (Test-Path $DotNetGlobalFile) { 50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 52 | $DotNetVersion = $DotNetGlobal.sdk.version 53 | } 54 | } 55 | 56 | # Install by channel or version 57 | $DotNetDirectory = "$TempDirectory\dotnet-win" 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 70 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="Current" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | export DOTNET_MULTILEVEL_LOOKUP=0 22 | 23 | ########################################################################### 24 | # EXECUTION 25 | ########################################################################### 26 | 27 | function FirstJsonValue { 28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 29 | } 30 | 31 | # If dotnet CLI is installed globally and it matches requested version, use for execution 32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 33 | export DOTNET_EXE="$(command -v dotnet)" 34 | else 35 | # Download install script 36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 37 | mkdir -p "$TEMP_DIRECTORY" 38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 39 | chmod +x "$DOTNET_INSTALL_FILE" 40 | 41 | # If global.json exists, load expected version 42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 44 | if [[ "$DOTNET_VERSION" == "" ]]; then 45 | unset DOTNET_VERSION 46 | fi 47 | fi 48 | 49 | # Install by channel or version 50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 51 | if [[ -z ${DOTNET_VERSION+x} ]]; then 52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 53 | else 54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 55 | fi 56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 57 | fi 58 | 59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" 60 | 61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 63 | -------------------------------------------------------------------------------- /build/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_style_qualification_for_field = false:warning 3 | dotnet_style_qualification_for_property = false:warning 4 | dotnet_style_qualification_for_method = false:warning 5 | dotnet_style_qualification_for_event = false:warning 6 | dotnet_style_require_accessibility_modifiers = never:warning 7 | 8 | csharp_style_expression_bodied_methods = true:silent 9 | csharp_style_expression_bodied_properties = true:warning 10 | csharp_style_expression_bodied_indexers = true:warning 11 | csharp_style_expression_bodied_accessors = true:warning 12 | -------------------------------------------------------------------------------- /build/Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Nuke.Common; 3 | using Nuke.Common.CI.AppVeyor; 4 | using Nuke.Common.CI.AzurePipelines; 5 | using Nuke.Common.CI.GitHubActions; 6 | using Nuke.Common.Execution; 7 | using Nuke.Common.IO; 8 | using Nuke.Common.ProjectModel; 9 | using Nuke.Common.Tooling; 10 | using Nuke.Common.Tools.DotNet; 11 | using Nuke.Common.Tools.GitVersion; 12 | using Nuke.Common.Tools.ReportGenerator; 13 | using Nuke.Common.Tools.SonarScanner; 14 | using Nuke.Common.Utilities.Collections; 15 | using static Nuke.Common.IO.FileSystemTasks; 16 | using static Nuke.Common.Tools.DotNet.DotNetTasks; 17 | 18 | [CheckBuildProjectConfigurations] 19 | [UnsetVisualStudioEnvironmentVariables] 20 | // NUKE CI Integration: http://www.nuke.build/docs/authoring-builds/ci-integration.html 21 | [GitHubActions("build", GitHubActionsImage.WindowsLatest, 22 | AutoGenerate = false, 23 | InvokedTargets = new[] { nameof(CiBuild) })] 24 | 25 | // cf.: https://github.com/nuke-build/nuke/blob/develop/azure-pipelines.yml 26 | [AzurePipelines(AzurePipelinesImage.WindowsLatest, 27 | AutoGenerate = false, 28 | InvokedTargets = new[] { nameof(CiBuild) })] 29 | // cf.: https://github.com/nuke-build/nuke/blob/develop/appveyor.yml 30 | [AppVeyor(AppVeyorImage.VisualStudioLatest, 31 | AutoGenerate = false, 32 | InvokedTargets = new[] { nameof(CiBuild) })] 33 | // ReSharper disable once CheckNamespace 34 | class Build : NukeBuild 35 | { 36 | //------------------------------------------------------------- 37 | // ReSharper disable once UnusedMember.Local 38 | Target Clean => _ => _ 39 | .Before(Restore) 40 | .Executes(() => 41 | { 42 | TestsDirectory 43 | .GlobDirectories("**/bin", "**/obj", "**/TestResults") 44 | .ForEach(DeleteDirectory); 45 | }); 46 | 47 | Target Restore => _ => _ 48 | .Executes(() => 49 | { 50 | DotNetRestore(s => s 51 | .SetProjectFile(Solution)); 52 | }); 53 | 54 | Target Compile => _ => _ 55 | .DependsOn(Restore) 56 | .Executes(() => 57 | { 58 | DotNetBuild(s => s 59 | .SetProjectFile(Solution) 60 | .SetConfiguration(Configuration) 61 | .SetAssemblyVersion(GitVersion.AssemblySemVer) 62 | .SetFileVersion(GitVersion.AssemblySemFileVer) 63 | .SetInformationalVersion(GitVersion.InformationalVersion) 64 | .EnableNoRestore() 65 | ); 66 | }); 67 | 68 | //------------------------------------------------------------- 69 | Target Test => _ => _ 70 | .DependsOn(Compile) 71 | .Executes(() => 72 | { 73 | DotNetTest(settings => settings 74 | .SetProjectFile(Solution) 75 | .SetConfiguration(Configuration) 76 | .SetFramework(Framework) 77 | .SetDataCollector("XPlat Code Coverage") 78 | ); 79 | }); 80 | 81 | Target Coverage => _ => _ 82 | .DependsOn(Test) 83 | .Executes(() => 84 | { 85 | ReportGeneratorTasks.ReportGenerator(settings => settings 86 | .SetFramework(Framework) 87 | .SetReports("**/coverage.cobertura.xml") 88 | .SetReportTypes(ReportTypes.Cobertura, ReportTypes.SonarQube, ReportTypes.Html) 89 | .SetTargetDirectory(".coverage/") 90 | ); 91 | }); 92 | 93 | //------------------------------------------------------------- 94 | Target Sonar => _ => _ 95 | .Description("SonarQube analysis") 96 | .DependsOn(SonarBegin) 97 | .DependsOn(Coverage) 98 | .DependsOn(SonarEnd) 99 | .Executes(() => 100 | { 101 | }); 102 | 103 | Target SonarBegin => _ => _ 104 | .Before(Test) 105 | .Executes(() => 106 | { 107 | SonarScannerTasks.SonarScannerBegin(settings => settings 108 | .SetProjectKey("fluentassertions.FluentAssertions.Autofac") 109 | .SetName("FluentAssertions.Autofac") 110 | .SetVersion(GitVersion.FullSemVer) 111 | .SetVSTestReports("**/*.trx") 112 | // cf.: https://github.com/nuke-build/nuke/issues/377#issuecomment-595276623 113 | .SetFramework("net5.0") //Framework) 114 | .SetLogin(SonarLogin) // TODO: should be secret -> SetArgument 115 | .SetServer(SonarServer) 116 | .SetProcessArgumentConfigurator(args => 117 | //.SetArgumentConfigurator(args => 118 | { 119 | // monorepo hack: tell Sonar to scan sub-project only 120 | args.Add($"/d:sonar.projectBaseDir={RootDirectory}"); 121 | // generic coverage data, cf.: https://docs.sonarqube.org/latest/analysis/generic-test/ 122 | args.Add("/d:sonar.coverageReportPaths=.coverage/SonarQube.xml"); 123 | if (!string.IsNullOrWhiteSpace(SonarLogin)) 124 | { 125 | // set organization & branch name, cf.: 126 | // - https://github.com/nuke-build/nuke/issues/304#issuecomment-522250591 127 | // - http://www.nuke.build/docs/authoring-builds/cli-tools.html#custom-arguments 128 | args.Add($"/o:{SonarOrganization}"); 129 | if (GitVersion.BranchName != "main") 130 | args.Add($"/d:sonar.branch.name={GitVersion.BranchName}"); 131 | } 132 | 133 | return args; 134 | }) 135 | ); 136 | }); 137 | 138 | Target SonarEnd => _ => _ 139 | .After(Coverage) 140 | .Executes(() => 141 | { 142 | SonarScannerTasks.SonarScannerEnd(settings => settings 143 | .SetLogin(SonarLogin) // TODO: should be secret -> SetArgument 144 | // cf.: https://github.com/nuke-build/nuke/issues/377#issuecomment-595276623 145 | .SetFramework("net5.0") // Framework 146 | ); 147 | }); 148 | 149 | //------------------------------------------------------------- 150 | Target Package => _ => 151 | { 152 | return _ 153 | .DependsOn(Test) 154 | .Produces(ArtifactsDir / "*.nupkg") 155 | .Executes(() => 156 | { 157 | DotNetPack(settings => settings 158 | .SetOutputDirectory(ArtifactsDir) 159 | .SetConfiguration(Configuration) 160 | .SetVersion(GitVersion.NuGetVersion) 161 | .SetNoWarns(5105) 162 | ); 163 | }); 164 | }; 165 | 166 | Target Push => _ => _ 167 | .DependsOn(Package) 168 | .Executes(() => 169 | { 170 | DotNetNuGetPush(settings => settings 171 | .SetTargetPath(ArtifactsDir / "*.nupkg") 172 | .SetApiKey(NuGetApiKey) 173 | .SetSource(NuGetSource) 174 | ); 175 | }); 176 | 177 | //------------------------------------------------------------- 178 | // ReSharper disable once UnusedMember.Local 179 | Target CiBuild => _ => _ 180 | .Description("DevOps build target") 181 | .DependsOn(IsPushTag ? new []{Sonar,Push}: new []{Test}) 182 | .Executes(() => 183 | { 184 | }); 185 | 186 | /// Support plugins are available for: 187 | /// - JetBrains ReSharper https://nuke.build/resharper 188 | /// - JetBrains Rider https://nuke.build/rider 189 | /// - Microsoft VisualStudio https://nuke.build/visualstudio 190 | /// - Microsoft VSCode https://nuke.build/vscode 191 | public static int Main() => Execute(x => x.Test); 192 | 193 | #region Parameters 194 | 195 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] 196 | readonly string Configuration = IsLocalBuild ? "Debug" : "Release"; 197 | 198 | [Solution] readonly Solution Solution; 199 | 200 | const string Framework = "net6.0"; 201 | [GitVersion(Framework = Framework, NoFetch = true)] readonly GitVersion GitVersion; 202 | 203 | [Parameter("The SonarQube login token")] 204 | readonly string SonarLogin = Environment.GetEnvironmentVariable("SONAR_LOGIN"); 205 | 206 | [Parameter("The SonarQube server")] 207 | readonly string SonarServer = IsLocalBuild ? "http://localhost:9000" : "https://sonarcloud.io"; 208 | 209 | [Parameter("The SonarQube organization")] readonly string SonarOrganization = "awesome-inc"; 210 | 211 | 212 | [Parameter("Enable coverlet diagnostics (log.*.txt)")] readonly bool CoverletDiag; 213 | 214 | [Parameter("Is CI Build")] readonly bool IsCiBuild = Host is GitHubActions; 215 | 216 | [Parameter("Push built NuGet package")] 217 | readonly bool IsPushTag = (Environment.GetEnvironmentVariable("GITHUB_REF") ?? "-unset-").StartsWith("refs/tags/"); 218 | 219 | [Parameter("NuGet API Key")] readonly string NuGetApiKey = Environment.GetEnvironmentVariable("NUGET_API_KEY"); 220 | [Parameter("NuGet Source")] readonly string NuGetSource = "https://www.nuget.org"; 221 | 222 | static readonly AbsolutePath TestsDirectory = RootDirectory / "tests"; 223 | static readonly AbsolutePath ArtifactsDir = RootDirectory / ".artifacts"; 224 | 225 | #endregion 226 | } 227 | -------------------------------------------------------------------------------- /build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | CS0649;CS0169 7 | .. 8 | .. 9 | 1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /build/_build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | Implicit 7 | Implicit 8 | ExpressionBody 9 | 0 10 | NEXT_LINE 11 | True 12 | False 13 | 120 14 | IF_OWNER_IS_SINGLE_LINE 15 | WRAP_IF_LONG 16 | False 17 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 19 | True 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | True 27 | True 28 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.101", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": true 6 | } 7 | } -------------------------------------------------------------------------------- /key.public: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluentassertions/fluentassertions.autofac/4bd1dc519e274b6963ec4dbd24056e99fdb5a858/key.public -------------------------------------------------------------------------------- /key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluentassertions/fluentassertions.autofac/4bd1dc519e274b6963ec4dbd24056e99fdb5a858/key.snk -------------------------------------------------------------------------------- /solution.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | latest 5 | full 6 | prompt 7 | 4 8 | 9 | 10 | 11 | 12 | FluentAssertions and Contributors 13 | FluentAssertions.Autofac 14 | FluentAssertions 15 | en 16 | Copyright © FluentAssertions 2017-2020 17 | Fluent Assertions extensions for Autofac 18 | Apache-2.0 19 | 20 | autofac;fluent-assertions;ioc;tdd;dotnet-core 21 | https://github.com/fluentassertions/fluentassertions.autofac 22 | 23 | false 24 | 25 | true 26 | $(SolutionDir)\key.snk 27 | 28 | 29 | --------------------------------------------------------------------------------