Skip to content

Instantly share code, notes, and snippets.

@domartynov
Last active June 25, 2018 16:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domartynov/6cbb7829d874aa47a4b1aaf716a96e4c to your computer and use it in GitHub Desktop.
Save domartynov/6cbb7829d874aa47a4b1aaf716a96e4c to your computer and use it in GitHub Desktop.
Reusable test dir per test logic added via [TestDir.Use] for xunit
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using FluentAssertions;
using Xunit;
using Xunit.Sdk;
namespace ExcelReporting.Tests.Common
{
public class TestDir
{
private static readonly Dictionary<string, int> TempDirs = new Dictionary<string, int>();
private readonly bool _autoMakeDir;
private string _path;
public MethodInfo TestMethod { get; private set; }
public DateTime Now { get; private set; }
public TestDir(MethodInfo testMethod, bool autoMakeDir)
{
TestMethod = testMethod;
_autoMakeDir = autoMakeDir;
}
public static string Path(string subdir = null)
{
var testDir = UseAttribute.TestDirSlot.Value ?? throw new InvalidOperationException("Not found current test marked TestDir.Use");
return testDir.PathInternal(subdir);
}
private string PathInternal(string subdir = null)
{
var path = subdir != null ? System.IO.Path.Combine(GetPath(), subdir) : GetPath();
return _autoMakeDir ? MkDirs(path) : path;
}
private string GetPath()
{
if (_path != null) return _path;
lock (TempDirs)
{
Now = DateTime.Now;
var path = $"_temp\\{Now:yyyyMMdd_HHmmss}_{TestMethod.ReflectedType?.Name ?? ""}_{TestMethod.Name}\\";
if (TempDirs.TryGetValue(path, out var cnt))
{
TempDirs[path] = cnt + 1;
return _path = $"{path}_{cnt}";
}
TempDirs[path] = 1;
return _path = path;
}
}
public static string MkDirs(string path)
{
var folder = System.IO.Path.GetDirectoryName(path);
if (folder != null && !Directory.Exists(folder)) Directory.CreateDirectory(folder);
return path;
}
public class UseAttribute : BeforeAfterTestAttribute
{
internal static readonly AsyncLocal<TestDir> TestDirSlot = new AsyncLocal<TestDir>();
private readonly bool _autoMakeDir;
public UseAttribute(bool autoMakeDir = false) => _autoMakeDir = autoMakeDir;
public override void Before(MethodInfo methodUnderTest) => TestDirSlot.Value = new TestDir(methodUnderTest, _autoMakeDir);
public override void After(MethodInfo methodUnderTest) => TestDirSlot.Value = null;
}
}
public class TestDirTests
{
[Fact]
public void ThrowWhenOutOfContext()
{
Action a = () => TestDir.Path();
a.ShouldThrow<InvalidOperationException>();
}
[Fact, TestDir.Use]
public void TestMethodFoo() =>
TestDir.Path().Should().Contain(nameof(TestMethodFoo)).And.Contain(GetType().Name);
}
public class TestDirTwoTests : TestDirTests
{
[Fact, TestDir.Use(true)]
public void TestAutoCreate() => Directory.Exists(TestDir.Path()).Should().BeTrue();
}
[TestDir.Use]
public class TestDirOnTestClass
{
[Fact]
public void TestMethod1Foo() =>
TestDir.Path().Should().Contain(nameof(TestMethod1Foo)).And.Contain(GetType().Name);
[Fact]
public void TestMethod2Foo() =>
TestDir.Path().Should().Contain(nameof(TestMethod2Foo)).And.Contain(GetType().Name);
}
public class Foo
{
public string Dir { get; }
public Foo(string dir) => Dir = dir;
}
[TestDir.Use]
public class TestSharedComponentOnTestDir
{
// have to use Lazy because xunit calls BeforeAfterTestAttribute.Before after ctor :(, see https://github.com/xunit/xunit/issues/581
private readonly Lazy<Foo> _fooLazy = new Lazy<Foo>(() => new Foo(TestDir.Path()));
protected Foo Foo => _fooLazy.Value;
[Fact]
public void TestFoo() => Foo.Dir.Should().Contain("TestFoo");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment