Skip to content

fix: respect TUnitImplicitUsings set in Directory.Build.props#5225

Merged
thomhurst merged 3 commits intomainfrom
fix/implicit-usings-directory-build-props
Mar 23, 2026
Merged

fix: respect TUnitImplicitUsings set in Directory.Build.props#5225
thomhurst merged 3 commits intomainfrom
fix/implicit-usings-directory-build-props

Conversation

@thomhurst
Copy link
Owner

Summary

Fixes #5208

  • TUnit.props and TUnit.Assertions.props unconditionally set TUnitImplicitUsings and TUnitAssertionsImplicitUsings to true, overwriting any value set earlier by Directory.Build.props (which is imported before NuGet package .props files in MSBuild evaluation order)
  • Added Condition="'$(Property)' == ''" guards so these properties only default to true when not already defined by the user
  • This matches the pattern already used in TUnit.Mocks.props

Test plan

  • Set TUnitImplicitUsings=false and TUnitAssertionsImplicitUsings=false in a Directory.Build.props alongside an NUnit+TUnit project and verify it builds without ambiguous reference errors
  • Verify default behavior (no explicit property set) still adds implicit usings

Add PowerShell scripts for measuring and comparing TUnit startup
performance, useful for tracking JIT overhead improvements:

- measure-startup.ps1: wall-clock timing with statistical analysis
- count-generated-methods.ps1: static analysis of generated method counts
- compare-branches.ps1: side-by-side branch comparison
- measure-jit.ps1: JIT trace collection and generated method analysis
Add conditions to avoid overwriting user-set property values, matching
the pattern already used in TUnit.Mocks.props.
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a clean, minimal, and correct fix for issue #5208.

What the fix does

MSBuild evaluates NuGet package .props files after Directory.Build.props, so properties set unconditionally in TUnit.props/TUnit.Assertions.props were silently overwriting any user-defined values. Adding the Condition="'$(Property)' == ''" guard (the MSBuild idiom for "only set if not already defined") correctly implements opt-out behavior.

Changes look correct

TUnit/TUnit.props — Both properties now use the inline condition form on the property element itself:

<TUnitImplicitUsings Condition="'$(TUnitImplicitUsings)' == ''">true</TUnitImplicitUsings>

This is idiomatic MSBuild and matches the pattern in TUnit.Mocks.props.

TUnit.Assertions/TUnit.Assertions.props — The condition is placed on the <PropertyGroup> rather than the individual property, which is equivalent since the group only contains one property. Both approaches are correct MSBuild.

Consistency

The fix aligns with the existing pattern in TUnit.Mocks.props:

<TUnitMockImplicitUsings Condition="'$(TUnitMockImplicitUsings)' == ''">enable</TUnitMockImplicitUsings>

Good to have all three packages now follow the same convention.

One observation (not a blocker)

In TUnit.Assertions.props, the outer XunitPackageReferenceFound condition is evaluated at MSBuild static evaluation time (before any targets run), at which point it always equals false (its initial value from the same props file). The actual xunit detection happens in a <Target> which runs later. This means the xunit guard on the PropertyGroup may not behave as intended in all scenarios — but this is a pre-existing issue unrelated to this PR and warrants a separate investigation.

Summary

The fix is correct, minimal, and consistent with existing patterns. Approving.

@thomhurst thomhurst merged commit 1575e72 into main Mar 23, 2026
14 of 15 checks passed
@thomhurst thomhurst deleted the fix/implicit-usings-directory-build-props branch March 23, 2026 02:12
intellitect-bot pushed a commit to IntelliTect/EssentialCSharp.Web that referenced this pull request Mar 25, 2026
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.21.6 to
1.21.20.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.21.20

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.20 -->

## What's Changed
### Other Changes
* fix: respect TUnitImplicitUsings set in Directory.Build.props by
@​thomhurst in thomhurst/TUnit#5225
* feat: covariant assertions for interfaces and non-sealed classes by
@​thomhurst in thomhurst/TUnit#5226
* feat: support string-to-parseable type conversions in [Arguments] by
@​thomhurst in thomhurst/TUnit#5227
* feat: add string length range assertions by @​thomhurst in
thomhurst/TUnit#4935
* Fix BeforeEvery/AfterEvery hooks for Class and Assembly not being
executed by @​Copilot in thomhurst/TUnit#5239
### Dependencies
* chore(deps): update tunit to 1.21.6 by @​thomhurst in
thomhurst/TUnit#5228
* chore(deps): update dependency gitversion.msbuild to 6.7.0 by
@​thomhurst in thomhurst/TUnit#5229
* chore(deps): update dependency gitversion.tool to v6.7.0 by
@​thomhurst in thomhurst/TUnit#5230
* chore(deps): update aspire to 13.2.0 - autoclosed by @​thomhurst in
thomhurst/TUnit#5232
* chore(deps): update dependency typescript to v6 by @​thomhurst in
thomhurst/TUnit#5233
* chore(deps): update dependency polyfill to 9.23.0 by @​thomhurst in
thomhurst/TUnit#5235
* chore(deps): update dependency polyfill to 9.23.0 by @​thomhurst in
thomhurst/TUnit#5236


**Full Changelog**:
thomhurst/TUnit@v1.21.6...v1.21.20

Commits viewable in [compare
view](thomhurst/TUnit@v1.21.6...v1.21.20).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit&package-manager=nuget&previous-version=1.21.6&new-version=1.21.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TUnitImplicitUsings / TUnitAssertionsImplicitUsings not respected in Directory.Build.props

1 participant