Add AssemblyFixture to our test infra
This is a feature that we're using in Templates and Blazor E2E tests to manage selenium.
It's a general purpose kind of thing, so it makes sense to make it more general. This requires using the
`[assembly: TestFramework()]`.
Also fixed a bug here where this feature broke collection fixtures.
\n\nCommit migrated from 208d44a985
This commit is contained in:
parent
b1987c75cb
commit
aadc979baf
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class AspNetTestAssemblyRunner : XunitTestAssemblyRunner
|
||||
{
|
||||
private readonly Dictionary<Type, object> _assemblyFixtureMappings = new Dictionary<Type, object>();
|
||||
|
||||
public AspNetTestAssemblyRunner(
|
||||
ITestAssembly testAssembly,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageSink executionMessageSink,
|
||||
ITestFrameworkExecutionOptions executionOptions)
|
||||
: base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task AfterTestAssemblyStartingAsync()
|
||||
{
|
||||
await base.AfterTestAssemblyStartingAsync();
|
||||
|
||||
// Find all the AssemblyFixtureAttributes on the test assembly
|
||||
Aggregator.Run(() =>
|
||||
{
|
||||
var fixturesAttributes = ((IReflectionAssemblyInfo)TestAssembly.Assembly)
|
||||
.Assembly
|
||||
.GetCustomAttributes(typeof(AssemblyFixtureAttribute), false)
|
||||
.Cast<AssemblyFixtureAttribute>()
|
||||
.ToList();
|
||||
|
||||
// Instantiate all the fixtures
|
||||
foreach (var fixtureAttribute in fixturesAttributes)
|
||||
{
|
||||
var ctorWithDiagnostics = fixtureAttribute.FixtureType.GetConstructor(new[] { typeof(IMessageSink) });
|
||||
if (ctorWithDiagnostics != null)
|
||||
{
|
||||
_assemblyFixtureMappings[fixtureAttribute.FixtureType] = Activator.CreateInstance(fixtureAttribute.FixtureType, DiagnosticMessageSink);
|
||||
}
|
||||
else
|
||||
{
|
||||
_assemblyFixtureMappings[fixtureAttribute.FixtureType] = Activator.CreateInstance(fixtureAttribute.FixtureType);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override Task BeforeTestAssemblyFinishedAsync()
|
||||
{
|
||||
// Dispose fixtures
|
||||
foreach (var disposable in _assemblyFixtureMappings.Values.OfType<IDisposable>())
|
||||
{
|
||||
Aggregator.Run(disposable.Dispose);
|
||||
}
|
||||
|
||||
return base.BeforeTestAssemblyFinishedAsync();
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestCollectionAsync(
|
||||
IMessageBus messageBus,
|
||||
ITestCollection testCollection,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new AspNetTestCollectionRunner(
|
||||
_assemblyFixtureMappings,
|
||||
testCollection,
|
||||
testCases,
|
||||
DiagnosticMessageSink,
|
||||
messageBus,
|
||||
TestCaseOrderer,
|
||||
new ExceptionAggregator(Aggregator),
|
||||
cancellationTokenSource).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class AspNetTestCollectionRunner : XunitTestCollectionRunner
|
||||
{
|
||||
private readonly IDictionary<Type, object> _assemblyFixtureMappings;
|
||||
private readonly IMessageSink _diagnosticMessageSink;
|
||||
|
||||
public AspNetTestCollectionRunner(
|
||||
Dictionary<Type, object> assemblyFixtureMappings,
|
||||
ITestCollection testCollection,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
ITestCaseOrderer testCaseOrderer,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
: base(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource)
|
||||
{
|
||||
_assemblyFixtureMappings = assemblyFixtureMappings;
|
||||
_diagnosticMessageSink = diagnosticMessageSink;
|
||||
}
|
||||
|
||||
protected override async Task AfterTestCollectionStartingAsync()
|
||||
{
|
||||
await base.AfterTestCollectionStartingAsync();
|
||||
|
||||
// note: We pass the assembly fixtures into the runner as ICollectionFixture<> - this seems to work OK without any
|
||||
// drawbacks. It's reasonable that we could add IAssemblyFixture<> and related plumbing if it ever became required.
|
||||
//
|
||||
// The reason for assembly fixture is when we want to start/stop something as the project scope - tests can only be
|
||||
// in one test collection at a time.
|
||||
foreach (var mapping in _assemblyFixtureMappings)
|
||||
{
|
||||
CollectionFixtureMappings.Add(mapping.Key, mapping.Value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task BeforeTestCollectionFinishedAsync()
|
||||
{
|
||||
// We need to remove the assembly fixtures so they won't get disposed.
|
||||
foreach (var mapping in _assemblyFixtureMappings)
|
||||
{
|
||||
CollectionFixtureMappings.Remove(mapping.Key);
|
||||
}
|
||||
|
||||
return base.BeforeTestCollectionFinishedAsync();
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, IEnumerable<IXunitTestCase> testCases)
|
||||
{
|
||||
var caste = testCases.ToArray();
|
||||
var type = caste.First().GetType();
|
||||
return base.RunTestClassAsync(testClass, @class, testCases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class AspNetTestFramework : XunitTestFramework
|
||||
{
|
||||
public AspNetTestFramework(IMessageSink messageSink)
|
||||
: base(messageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
|
||||
=> new AspNetTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class AspNetTestFrameworkExecutor : XunitTestFrameworkExecutor
|
||||
{
|
||||
public AspNetTestFrameworkExecutor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink)
|
||||
: base(assemblyName, sourceInformationProvider, diagnosticMessageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions)
|
||||
{
|
||||
using (var assemblyRunner = new AspNetTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions))
|
||||
{
|
||||
await assemblyRunner.RunAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public class AssemblyFixtureAttribute : Attribute
|
||||
{
|
||||
public AssemblyFixtureAttribute(Type fixtureType)
|
||||
{
|
||||
FixtureType = fixtureType;
|
||||
}
|
||||
|
||||
public Type FixtureType { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
// We include a collection and assembly fixture to verify that they both still work.
|
||||
[Collection("MyCollection")]
|
||||
[TestCaseOrderer("Microsoft.AspNetCore.Testing.AlphabeticalOrderer", "Microsoft.AspNetCore.Testing.Tests")]
|
||||
public class AssemblyFixtureTest
|
||||
{
|
||||
public AssemblyFixtureTest(TestAssemblyFixture assemblyFixture, TestCollectionFixture collectionFixture)
|
||||
{
|
||||
AssemblyFixture = assemblyFixture;
|
||||
CollectionFixture = collectionFixture;
|
||||
}
|
||||
|
||||
public TestAssemblyFixture AssemblyFixture { get; }
|
||||
public TestCollectionFixture CollectionFixture { get; }
|
||||
|
||||
[Fact]
|
||||
public void A()
|
||||
{
|
||||
Assert.NotNull(AssemblyFixture);
|
||||
Assert.Equal(0, AssemblyFixture.Count);
|
||||
|
||||
Assert.NotNull(CollectionFixture);
|
||||
Assert.Equal(0, CollectionFixture.Count);
|
||||
|
||||
AssemblyFixture.Count++;
|
||||
CollectionFixture.Count++;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void B()
|
||||
{
|
||||
Assert.Equal(1, AssemblyFixture.Count);
|
||||
Assert.Equal(1, CollectionFixture.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[CollectionDefinition("MyCollection", DisableParallelization = true)]
|
||||
public class MyCollection : ICollectionFixture<TestCollectionFixture>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
[assembly: AssemblyFixture(typeof(TestAssemblyFixture))]
|
||||
[assembly: TestFramework("Microsoft.AspNetCore.Testing.AspNetTestFramework", "Microsoft.AspNetCore.Testing")]
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class TestAssemblyFixture
|
||||
{
|
||||
public int Count { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
public class TestCollectionFixture
|
||||
{
|
||||
public int Count { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue