SimpleTestRunner для теста MS - PullRequest
0 голосов
/ 13 апреля 2020

В NUnit есть класс SimpleTestRunner для выполнения одного простого теста. Вот что делает c: Код GitHub

Существует ли подобный класс или функциональность при использовании MS Test Framework?


Существует простой способ загрузить сборку с помощью Assembly.LoadFrom, а затем просто выполнить поиск по типу и вызвать функцию тестирования. Но этот механизм не обрабатывает [AssemblyInitialize], [TestInitialize] и [DataRow] отличает все остальные. И неправильно обрабатывает путь, когда тест async.

Итак, вопрос, есть ли возможность запустить функцию Ms Test Test из кода? Любое Opensource или официальное решение приветствуется:)

1 Ответ

0 голосов
/ 13 апреля 2020

Я не нашел никакого решения, поэтому я реализую небольшое решение, которое работает хорошо, но не обрабатывает следующие случаи:

  • Not Calling the TestCleanup
  • Not Callsing Очистка сборки
  • Не обрабатывается, когда тест async Task
  • Не обрабатывает атрибут DataRow

Но для первоначального решения все в порядке. Вот код этого:

/// <summary>
/// This class is able to run a test function from a test assembly.
/// This is used for automated test possibilities. This class is not for production code.
/// The <see cref="ExecuteTest"/> can be ca;lled many times you want, the initizalization will be done automatically
/// The class not handle all Ms Test feature, just the following:
///     - <see cref="AssemblyInitializeAttribute"/> 
/// </summary>
public class TestFunctionRunner
{
    /// <summary>
    /// The path of the dll file, which is contains the test function 
    /// </summary>
    public string AssemblyPath { get; }
    /// <summary>
    /// The instance of the the test assembly
    /// </summary>
    public Assembly TestAssembly { get; private set; }
    /// <summary>
    /// Name of the test class, which contains the the test function
    /// </summary>
    public string TestClassName { get; }
    /// <summary>
    /// The <see cref="Type"/> of the test class 
    /// </summary>
    public Type TestClassType { get; private set; }
    /// <summary>
    /// The created instance of the TestClass
    /// </summary>
    public object TestClassInstance { get; private set; }
    /// <summary>
    /// Name of the test function which will be executed each time
    /// </summary>
    public string TestFunctionName { get; }


    private bool IsAssemblyInitialized = false;
    /// <summary>
    /// Construct a test runner class, to execute a test function from code.
    /// </summary>
    /// <param name="assemblyPath">The path of the dll file, which is contains the test function </param>
    /// <param name="testClass">Name of the test class, which contains the the test function</param>
    /// <param name="testFunction"> Name of the test function which will be executed each time</param>
    public TestFunctionRunner( string assemblyPath, string testClass, string testFunction )
    {
        AssemblyPath = assemblyPath;
        TestClassName = testClass;
        TestFunctionName = testFunction;

        InitializeRunner();
    }

    private void InitializeRunner()
    {
        TestAssembly = Assembly.LoadFrom( AssemblyPath );
        TestClassType = TestAssembly.DefinedTypes.FirstOrDefault( t => t.Name == TestClassName );
        if( TestClassType == null )
            throw new Exception( $"The class name: {TestClassName} not found in assembly {TestAssembly.FullName}" );

        TestClassInstance = Activator.CreateInstance( TestClassType );
    }

    /// <summary>
    /// Execute test given test
    /// </summary>
    public void ExecuteTest()
    {
        string workingDirectory = Directory.GetCurrentDirectory();
        if( string.IsNullOrEmpty( Path.GetDirectoryName( AssemblyPath ) ) == false )
            Directory.SetCurrentDirectory( Path.GetDirectoryName( AssemblyPath ) );

        if( IsAssemblyInitialized == false )
            InitializeAssembly();

        ExecuteTestInitialize();

        TestClassType.InvokeMember( name: TestFunctionName
                    , invokeAttr: BindingFlags.Default | BindingFlags.InvokeMethod
                    , binder: null
                    , target: TestClassInstance
                    , args: null );

        Directory.SetCurrentDirectory( workingDirectory );
    }

    private void InitializeAssembly()
    {
        IEnumerable<TypeInfo> testClasses = TestAssembly
            .DefinedTypes
            .Where( t => t.GetCustomAttribute<TestClassAttribute>() != null );

        MethodInfo assemblyInitializeMethod = null;
        foreach( TypeInfo testClass in testClasses )
        {
            assemblyInitializeMethod = testClass.GetMethods().FirstOrDefault( mi => mi.GetCustomAttribute<AssemblyInitializeAttribute>() != null );
            if( assemblyInitializeMethod != null )
                break;
        }

        if( assemblyInitializeMethod != null )
        {
            if( assemblyInitializeMethod.IsStatic == false )
                throw new Exception( $"Test initialize error: assembly initialize method : {assemblyInitializeMethod.Name} must be static " );

            if( assemblyInitializeMethod.GetParameters().Count() != 1 )
                throw new Exception( $"Test initialize error: assembly initialize method : {assemblyInitializeMethod.Name} must have one paramet, whe type is : {typeof(TestContext)}" );

            assemblyInitializeMethod.Invoke( null, new object[] {  new MyTestContext() } );
        }

        MethodInfo classInitializeMethod = TestClassType
            .GetMethods()
            .FirstOrDefault( m => m.GetCustomAttribute<ClassInitializeAttribute>() != null );

        if( classInitializeMethod != null )
        {
            if( classInitializeMethod.GetParameters().Count() > 0 )
                throw new Exception( $"Test initialize error: class initialize method : {classInitializeMethod.Name} must be parameterless " );

            classInitializeMethod.Invoke( TestClassInstance, null );
        }

        IsAssemblyInitialized = true;
    }

    private void ExecuteTestInitialize()
    {
        MethodInfo testInitializeMethod = TestClassType
            .GetMethods()
            .FirstOrDefault( m => m.GetCustomAttribute<TestInitializeAttribute>() != null );

        if( testInitializeMethod.GetParameters().Count() > 0 )
            throw new Exception( $"Test initialize error: test initialize method : {testInitializeMethod.Name} must be parameterless" );

        testInitializeMethod.Invoke( TestClassInstance, null );
    }


    private class MyTestContext : TestContext
    {
        public override IDictionary Properties => throw new NotImplementedException();

        public override void AddResultFile( string fileName )
        {
        }

        public override void WriteLine( string message )
        {
            Console.WriteLine( message );
            Debug.WriteLine( message );
        }

        public override void WriteLine( string format, params object[] args )
        {
            Console.WriteLine( format, args );
            Debug.WriteLine( format, args );
        }
    }
}
...