Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
525b9ef
First code commit
thomhurst Jan 25, 2024
ecdda9d
Pipeline fix
thomhurst Jan 25, 2024
1222d7c
Fixes
thomhurst Jan 25, 2024
ff1adb3
Fix
thomhurst Jan 25, 2024
e0256ed
Fix
thomhurst Jan 25, 2024
4ba0d5c
Redundant
thomhurst Jan 25, 2024
67a94ff
Remove debugger call
thomhurst Jan 25, 2024
94ffc9d
Fix
thomhurst Jan 25, 2024
7065bf3
Fix
thomhurst Jan 25, 2024
b92d126
Tweak
thomhurst Jan 25, 2024
1109f69
Fix
thomhurst Jan 25, 2024
65af120
Fix
thomhurst Jan 25, 2024
09ab4d9
Fix
thomhurst Jan 25, 2024
0c34995
Tweaks
thomhurst Jan 25, 2024
c6edd6c
Fix queuing tests logic
thomhurst Jan 25, 2024
2347fab
Skip logic fix
thomhurst Jan 25, 2024
aad383f
Tweaks
thomhurst Jan 25, 2024
86b006d
Fixes
thomhurst Jan 25, 2024
1cfd42a
Fix
thomhurst Jan 26, 2024
8d35c92
Fixes
thomhurst Jan 26, 2024
e488bcd
Fixes
thomhurst Jan 26, 2024
047693b
Even Odd Assertions
thomhurst Jan 26, 2024
b4e5a1d
Assertion tests
thomhurst Jan 26, 2024
7e7dee7
Engine project
thomhurst Jan 27, 2024
86c1ce0
Dependency Injection
thomhurst Jan 27, 2024
02cdc3e
Fix
thomhurst Jan 27, 2024
415553d
Fix
thomhurst Jan 27, 2024
075c996
Retry and Repeat Attributes
thomhurst Jan 27, 2024
aa7d484
TestClassCreator
thomhurst Jan 27, 2024
11d3809
Refactor
thomhurst Jan 27, 2024
ad02d8a
TestDataSource Tests
thomhurst Jan 27, 2024
c736d0e
Disposer.cs
thomhurst Jan 27, 2024
e8ea409
Use ID
thomhurst Jan 27, 2024
f4ae6f7
TUnitTestFilterProvider
thomhurst Jan 27, 2024
8a4854a
TestContext.cs
thomhurst Jan 27, 2024
e4d6c92
Throws conditions
thomhurst Jan 27, 2024
9f99080
Tweaks
thomhurst Jan 27, 2024
adbf995
Throws assertions
thomhurst Jan 27, 2024
b56e534
TimeoutAttribute.cs
thomhurst Jan 28, 2024
97c9784
TimeoutException.cs
thomhurst Jan 28, 2024
8a89f21
Tweak to constructing class
thomhurst Jan 28, 2024
144bac1
InternalsVisibleTo
thomhurst Jan 28, 2024
29dea9b
CurrentExecutionCount
thomhurst Jan 28, 2024
b084cd2
Rename to Clean Ups
thomhurst Jan 28, 2024
30cbf09
HasCountAssertCondition
thomhurst Jan 28, 2024
cc258da
Rework assertion generics
thomhurst Jan 29, 2024
c7541c5
Rework
thomhurst Jan 29, 2024
0b389c1
Rework again
thomhurst Jan 29, 2024
835c19e
Rework
thomhurst Jan 29, 2024
00408f2
Improvements
thomhurst Jan 29, 2024
cb8a765
Improvements
thomhurst Jan 29, 2024
b28b1fb
Rework
thomhurst Jan 29, 2024
531f2c5
Rework
thomhurst Jan 29, 2024
3e9e465
Rework
thomhurst Jan 29, 2024
9b1ac05
Analyzers project
thomhurst Jan 29, 2024
0b5f50b
Fix message generation
thomhurst Jan 29, 2024
3d25024
Analyzer
thomhurst Jan 29, 2024
8a3686d
Analyzer working
thomhurst Jan 30, 2024
a677a03
Fix messages in and/or conditions
thomhurst Jan 30, 2024
970bf6d
Fix messages in and/or conditions
thomhurst Jan 30, 2024
f44c281
And + Or Instance classes
thomhurst Jan 30, 2024
6ec0848
Remove NestedConditionsOperator
thomhurst Jan 30, 2024
e318574
OrIs AndIs OrHas AndHas
thomhurst Jan 30, 2024
59667d8
Length and Count assertions
thomhurst Jan 30, 2024
61e7537
Generic Property
thomhurst Jan 31, 2024
01e95e2
Tweaks
thomhurst Jan 31, 2024
6725ddc
Tweaks to generic assertion models
thomhurst Jan 31, 2024
ae792b6
Tweaks
thomhurst Jan 31, 2024
c2411cd
Rework access
thomhurst Jan 31, 2024
4b677ac
Fix for length and count strong types
thomhurst Feb 1, 2024
02c2fce
Tweaks
thomhurst Feb 1, 2024
628e7b1
Fix analyzer
thomhurst Feb 1, 2024
f114333
Rework
thomhurst Feb 2, 2024
257e22c
Overhaul assertions with AssertionBuilder.cs
thomhurst Feb 2, 2024
9a05050
Tweak namespaces
thomhurst Feb 2, 2024
44fe05e
Fix global usings
thomhurst Feb 2, 2024
7410c4f
Fix global usings
thomhurst Feb 2, 2024
ad4f817
Analyzer for awaiting assertion calls
thomhurst Feb 2, 2024
1d344c6
AwaitAssertionAnalyzer
thomhurst Feb 2, 2024
5aa04ba
Enumerable EquivalentTo
thomhurst Feb 2, 2024
6a07544
Does
thomhurst Feb 2, 2024
bbd305d
Remove DelegateAssertCondition.cs
thomhurst Feb 2, 2024
d5375a6
Remove redundant classes
thomhurst Feb 2, 2024
9d5c661
DelegateAssertCondition
thomhurst Feb 3, 2024
8dd8dc1
StringLength.cs
thomhurst Feb 3, 2024
246150d
EnumerableCount
thomhurst Feb 3, 2024
f9bb813
InvertedAssertCondition.cs
thomhurst Feb 3, 2024
899ef8d
Fix
thomhurst Feb 3, 2024
33b5556
Working Filter.cs
thomhurst Feb 3, 2024
fc64b4a
Rework filters
thomhurst Feb 3, 2024
3ada78a
Rework connectors to reduce duplication for and/or cases
thomhurst Feb 3, 2024
a9db720
StringLength.cs fixes
thomhurst Feb 3, 2024
84d8ad4
StringLength.cs fixes
thomhurst Feb 3, 2024
e95b121
Better message for and condition failures
thomhurst Feb 3, 2024
8b09d23
Not rename
thomhurst Feb 3, 2024
8d775da
Rework inverting logic
thomhurst Feb 3, 2024
db84fbd
Remove clutter
thomhurst Feb 3, 2024
ca8d9ee
Expose Does and Throws on And & Or objects
thomhurst Feb 3, 2024
89472b0
Move throws to DelegateAssertionBuilder
thomhurst Feb 3, 2024
cb96538
Refactor delegate builders
thomhurst Feb 3, 2024
70664ec
Generic And Or operators
thomhurst Feb 4, 2024
ec0da8b
Pass category in tests
thomhurst Feb 4, 2024
74a0b53
README.md
thomhurst Feb 4, 2024
3d41052
Re-work ID matching
thomhurst Feb 4, 2024
16ab883
Ability to run static tests
thomhurst Feb 4, 2024
ad3bebf
Fix unique ID
thomhurst Feb 4, 2024
28882a8
TUnitTestProperties
thomhurst Feb 4, 2024
6886370
Tweak
thomhurst Feb 4, 2024
bd90af5
Tweak display name
thomhurst Feb 4, 2024
279dc33
Unique ID
thomhurst Feb 4, 2024
a8b5654
Tweak count display name
thomhurst Feb 4, 2024
9c951bf
Fix UniqueId usage
thomhurst Feb 4, 2024
49b47a1
Tweak MatchTest logic
thomhurst Feb 4, 2024
286cc7d
Internal classes
thomhurst Feb 4, 2024
2c2b8b7
Directory.Build.props
thomhurst Feb 4, 2024
2201afb
Output
thomhurst Feb 4, 2024
baf6de8
Repeat tests run in parallel
thomhurst Feb 4, 2024
6516eb6
ExecuteWithRetries or ExecuteWithRepeats
thomhurst Feb 4, 2024
d2acd1b
Workflows
thomhurst Feb 4, 2024
9505e2f
Alpha UploadToNuGetModule.cs
thomhurst Feb 4, 2024
962e869
RunOnlyOnBranch
thomhurst Feb 4, 2024
23eb50d
Debug
thomhurst Feb 4, 2024
a3f4309
null warning
thomhurst Feb 4, 2024
0b2cc9b
Update modular pipelines
thomhurst Feb 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
open-pull-requests-limit: 20
schedule:
interval: "daily"
groups:
test-dependencies:
patterns:
- NUnit*
- "*Test*"
modularpipelines-dependencies:
patterns:
- "*ModularPipeline*"
ignore:
- dependency-name: "Microsoft.Extensions.*"
update-types: ["version-update:semver-major"]
39 changes: 39 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:
inputs:
publish-packages:
description: Publish packages?
type: boolean
required: true

jobs:
modularpipeline:
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Pull Requests' }}
runs-on: windows-latest

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x
- name: Run Pipeline
run: dotnet run -c Release
working-directory: "TUnit.Pipeline"
env:
DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Development' }}
NuGet__ApiKey: ${{ secrets.NUGET__APIKEY }}
PULL_REQUEST_BRANCH: ${{ github.event.pull_request.head.ref }}
PUBLISH_PACKAGES: ${{ github.event.inputs.publish-packages }}
13 changes: 13 additions & 0 deletions .idea/.idea.TUnit/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/.idea.TUnit/.idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/.idea.TUnit/.idea/indexLayout.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/.idea.TUnit/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>TUnit.Core</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>TUnit.Engine</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>TUnit</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>TUnit.TestAdapter</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# TUnit
# TUnit
T(est)Unit!

```csharp
[Test]
public async Task Test1()
{
var value = "Hello world!";

await Assert.That(value)
.Is.Not.Null
.And.Is.EqualTo("hello world!", StringComparison.InvariantCultureIgnoreCase)
.And.Has.Count().EqualTo(12)
.And.Does.StartWith("H");
}
```

## Motivations
There are only three main testing frameworks in the .NET world - xUnit, NUnit and MSTest.
More frameworks means more options, and more options motivates more features or improvements.

These testing frameworks are amazing, but I've had some issues with them. You might not have had any of these, but these are my experiences:

### xUnit
There is no way to tap into information about a test in a generic way.
For example, I've had some Playwright tests run before, and I want them to save a screenshot or video ONLY when the test fails.
If the test passes, I don't have anything to investigate, and it'll use up unnecessary storage, and it'll probably slow my test suite down if I had hundreds or thousands of tests all trying to save screenshots.

However, if I'm in a Dispose method which is called when the test ends, then there's no way for me to know if my test succeeded or failed. I'd have to do some really clunky workaround involving try catch and setting a boolean or exception to a class field and checking that. And to do that for every test was just not ideal.

#### Assertions
I have stumbled across assertions so many times where the arguments are the wrong way round.
This can result in really confusing error messages.
```csharp
var one = 2;
Assert.Equal(1, one)
Assert.Equal(one, 1)
```

### NUnit

#### Assertions
I absolutely love the newer assertion syntax in NUnit. The `Assert.That(something, Is.Something)`. I think it's really clear to read, it's clear what is being asserted, and it's clear what you're trying to achieve.

However, there is a lack of type checking on assertions. (Yes, there are analyzer packages to help with this, but this still isn't strict type checking.)

`Assert.That("1", Throws.Exception);`

This assertion makes no sense, because we're passing in a string. This can never throw an exception because it isn't a delegate that can be executed. But it's still perfectly valid code that will compile.

As does this:
`Assert.That(1, Does.Contain("Foo!"));`

With TUnit assertions, I wanted to make these impossible to compile. So type constraints are built into the assertions themselves. There should be no way for a non-delegate to be able to do a `Throws` assertion, or for an `int` assertion to check for `string` conditions.

So in TUnit, this will compile:

`await Assert.That(() => 1).Throws.Nothing;`

This won't:

`await Assert.That(1).Throws.Nothing;`
24 changes: 24 additions & 0 deletions TUnit.Analyzers/TUnit.Analyzers.Sample/Examples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global

using System.Threading.Tasks;
using TUnit.Assertions;

namespace TUnit.Analyzers.Sample;

// If you don't see warnings, build the Analyzers Project.

public class Examples
{
public class CommonClass // Try to apply quick fix using the IDE.
{
}

public async Task ToStars()
{
await Assert.That("1").Is.EqualTo("2");
var spaceship = new Spaceship();
spaceship.SetSpeed(300000000); // Invalid value, it should be highlighted.
spaceship.SetSpeed(42);
}
}
12 changes: 12 additions & 0 deletions TUnit.Analyzers/TUnit.Analyzers.Sample/Spaceship.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace TUnit.Analyzers.Sample;

public class Spaceship
{
public void SetSpeed(long speed)
{
if (speed > 299_792_458)
throw new ArgumentOutOfRangeException(nameof(speed));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\TUnit\TUnit.csproj" />
<ProjectReference Include="..\TUnit.Analyzers\TUnit.Analyzers.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="4.0.1" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Verifier = TUnit.Analyzers.Tests.Verifiers.CSharpAnalyzerVerifier<TUnit.Analyzers.AwaitAssertionAnalyzer>;

namespace TUnit.Analyzers.Tests;

public class AwaitAssertionAnalyzerTests
{
[Test]
public async Task ClassWithMyCompanyTitle_AlertDiagnostic()
{
const string text = """
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Core;

public class MyClass
{

public async Task MyTest()
{
var one = 1;
{|#0:Assert.That(one).Is.EqualTo(1);|}
}

}
""";

var expected = Verifier.Diagnostic().WithLocation(0);

await Verifier.VerifyAnalyzerAsync(text, expected).ConfigureAwait(false);
}
}
44 changes: 44 additions & 0 deletions TUnit.Analyzers/TUnit.Analyzers.Tests/Net.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Immutable;
using System.IO;
using Microsoft.CodeAnalysis.Testing;

namespace TUnit.Analyzers.Tests;

internal static class Net
{
private static readonly Lazy<ReferenceAssemblies> LazyNet60 = new(() =>
new ReferenceAssemblies(
"net6.0",
new PackageIdentity(
"Microsoft.NETCore.App.Ref",
"6.0.21"),
Path.Combine("ref", "net6.0"))
.AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.Extensions.Logging", "6.0.0")))
);

public static ReferenceAssemblies Net60 => LazyNet60.Value;

private static readonly Lazy<ReferenceAssemblies> LazyNet70 = new(() =>
new ReferenceAssemblies(
"net7.0",
new PackageIdentity(
"Microsoft.NETCore.App.Ref",
"7.0.15"),
Path.Combine("ref", "net7.0"))
.AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.Extensions.Logging", "7.0.0")))
);

private static readonly Lazy<ReferenceAssemblies> LazyNet80 = new(() =>
new ReferenceAssemblies(
"net8.0",
new PackageIdentity(
"Microsoft.NETCore.App.Ref",
"8.0.1"),
Path.Combine("ref", "net8.0"))
.AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.Extensions.Logging", "8.0.0")))
);

public static ReferenceAssemblies Net70 => LazyNet70.Value;
public static ReferenceAssemblies Net80 => LazyNet80.Value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// using System.Threading.Tasks;
// using Xunit;
// using Verifier =
// Microsoft.CodeAnalysis.CSharp.Testing.XUnit.CodeFixVerifier<TUnit.Analyzers.MixAndOrOperatorsAnalyzer,
// TUnit.Analyzers.SampleCodeFixProvider>;
//
// namespace TUnit.Analyzers.Tests;
//
// public class SampleCodeFixProviderTests
// {
// [Fact]
// public async Task ClassWithMyCompanyTitle_ReplaceWithCommonKeyword()
// {
// const string text = @"
// public class MyCompanyClass
// {
// }
// ";
//
// const string newText = @"
// public class CommonClass
// {
// }
// ";
//
// var expected = Verifier.Diagnostic()
// .WithLocation(2, 14)
// .WithArguments("MyCompanyClass");
// await Verifier.VerifyCodeFixAsync(text, expected, newText).ConfigureAwait(false);
// }
// }
29 changes: 29 additions & 0 deletions TUnit.Analyzers/TUnit.Analyzers.Tests/TUnit.Analyzers.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.NUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.NUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"/>
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\TUnit.Assertions\TUnit.Assertions.csproj" />
<ProjectReference Include="..\..\TUnit\TUnit.csproj" />
<ProjectReference Include="..\TUnit.Analyzers\TUnit.Analyzers.csproj"/>
</ItemGroup>

</Project>
Loading