简体   繁体   中英

SimpleTestRunner for MS Test

In NUnit there is a SimpleTestRunner class to execute one simple test. Here is the doc for it: GitHub Code

Is there a similar class or functionality when I use MS Test Framework?


There is a simple way, to load assembly with Assembly.LoadFrom and after just search the type and the Invoke a test function. But this mechanism does not handle the [AssemblyInitialize] , the [TestInitialize] and [DataRow] features all others. And does not handle the way correctly when the test is async .

So The question, is there any possibility to run an Ms Test Test function from code? Any Opensource or official solution is welcome:)

I did not found any solution, So I implement a little solution, which works well, but does not handle the following cases:

  • Not Calling the TestCleanup
  • Not Callsing The assemby cleanup
  • Does not handle when the test is async Task
  • Does not handle the DataRow attribute

But for initial solution is OK. Here is the code of it:

/// <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 );
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM