Подтвердить что ты не робот

Как запустить все тесты в решении

Кажется, что я могу запустить все свои тесты в решении за один раз из командной строки, используя MSTest, если я использую флаг /testmetadata, как описано здесь: http://msdn.microsoft.com/en-us/library/ms182487.aspx

Я запускаю тесты блока SQL Server DB в Visual Studio 2013, в котором у меня, похоже, нет файла vsmdi, и я тоже не могу найти способ добавить его. Я попытался создать файл testettings, но он не обнаруживает никаких тестов (показывает "Нет тестов для запуска" ) при вызове MSTest.

Есть ли способ, которым MSTest может запускать все мои тесты в решении VS2013?

4b9b3361

Ответ 1

Я хотел закрыть этот открытый вопрос. Мое намерение состояло в том, чтобы запускать все тесты за один раз из Hudson CI-сервера, поэтому я написал базовое консольное приложение, чтобы найти и вызывать MSTest во всех DLL файлах внутри папки решений. Это приложение выполняется после того, как проект построен в режиме выпуска.

string execId = null;
string className = null;
string testName = null;
string testResult = null;
string resultLine = null;
List<string> results = new List<string>();
XmlDocument resultsDoc = new XmlDocument();
XmlNode executionNode = null;
XmlNode testMethodNode = null;

// Define the test instance settings
Process testInstance = null;
ProcessStartInfo testInfo = new ProcessStartInfo()
{
    UseShellExecute = false,
    CreateNoWindow = true,
};

// Fetch project list from the disk
List<string> excluded = ConfigurationManager.AppSettings["ExcludedProjects"].Split(',').ToList();
DirectoryInfo assemblyPath = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
DirectoryInfo[] directories = assemblyPath.Parent.Parent.Parent.Parent.GetDirectories();

// Create a test worklist
List<string> worklist = directories.Where(t => !excluded.Contains(t.Name))
                                    .Select(t => String.Format(ConfigurationManager.AppSettings["MSTestCommand"], t.FullName, t.Name))
                                    .ToList();

// Start test execution
Console.WriteLine("Starting Execution...");
Console.WriteLine();

Console.WriteLine("Results               Top Level Tests");
Console.WriteLine("-------               ---------------");

// Remove any existing run results
if (File.Exists("UnitTests.trx"))
{
    File.Delete("UnitTests.trx");
}

// Run each project in the worklist
foreach (string item in worklist)
{
    testInfo.FileName = item;
    testInstance = Process.Start(testInfo);
    testInstance.WaitForExit();

    if (File.Exists("UnitTests.trx"))
    {
        resultsDoc = new XmlDocument();
        resultsDoc.Load("UnitTests.trx");

        foreach (XmlNode result in resultsDoc.GetElementsByTagName("UnitTestResult"))
        {
            // Get the execution ID for the test
            execId = result.Attributes["executionId"].Value;

            // Find the execution and test method nodes
            executionNode = resultsDoc.GetElementsByTagName("Execution")
                                        .OfType<XmlNode>()
                                        .Where(n => n.Attributes["id"] != null && n.Attributes["id"].Value.Equals(execId))
                                        .First();

            testMethodNode = executionNode.ParentNode
                                            .ChildNodes
                                            .OfType<XmlNode>()
                                            .Where(n => n.Name.Equals("TestMethod"))
                                            .First();

            // Get the class name, test name and result
            className = testMethodNode.Attributes["className"].Value.Split(',')[0];
            testName = result.Attributes["testName"].Value;
            testResult = result.Attributes["outcome"].Value;
            resultLine = String.Format("{0}                {1}.{2}", testResult, className, testName);

            results.Add(resultLine);
            Console.WriteLine(resultLine);
        }

        File.Delete("UnitTests.trx");
    }
}

// Calculate passed / failed test case count
int passed = results.Where(r => r.StartsWith("Passed")).Count();
int failed = results.Where(r => r.StartsWith("Failed")).Count();

// Print the summary
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine("-------");
Console.WriteLine("Test Run {0}", failed > 0 ? "Failed." : "Passed.");
Console.WriteLine();

if (passed > 0)
    Console.WriteLine("\tPassed {0,7}", passed);

if (failed > 0)
    Console.WriteLine("\tFailed {0,7}", failed);

Console.WriteLine("\t--------------");
Console.WriteLine("\tTotal {0,8}", results.Count);

if (failed > 0)
    Environment.Exit(-1);
else
    Environment.Exit(0);

Мой файл App.config:

<appSettings>
    <add key="ExcludedProjects" value="UnitTests.Bootstrap,UnitTests.Utils" />
    <add key="MSTestCommand" value="&quot;c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\MSTest.exe&quot; /testcontainer:&quot;{0}\bin\Release\{1}.dll&quot; /nologo /resultsfile:&quot;UnitTests.trx&quot;" />
</appSettings>

Ответ 2

MsTest - это своего рода "устаревшая" тестовая среда Visual Studio 2013. она по-прежнему используется для некоторых типов тестов, но многие другие тесты, которые могут быть выполнены, теперь живут в новом Agile Test Runner. Теперь SQL Server Unit Tests все еще находится в категории "Некоторые типы" и должен выполняться через MsTest.exe.

Самый простой способ - использовать /TestContainer переключатель командной строки и использовать шаблон именования для ваших тестовых проектов. Таким образом, вы можете быстро захватить все сборки с определенным шаблоном именования и затем передать их в MsTest. Простую команду powershell можно использовать для захвата всех файлов, которые привязаны к вашему шаблону, и затем передать их в командную строку.

vsmdi по-прежнему будет работать в Visual Studio 2013, но редактор был удален из инструмента, и для него больше нет шаблона. Так что это очень сложно использовать. Это то, что Microsoft должна сказать о VSDMI:

ВниманиеСписки тестов больше не поддерживаются в Visual Studio 2012:

  • Вы не можете создавать новые тестовые списки.
  • Вы не можете запускать тесты тестового списка из Visual Studio.
  • Если вы обновили Visual Studio 2010 и имели тестовый список в своем решении, вы можете продолжить его редактирование в Visual Studio.
  • Вы можете продолжить запуск тестового списка с помощью mstest.exe из командной строки, как описано выше.
  • Если вы использовали тестовый список в определении сборки, вы можете продолжать использовать его.

В основном они говорят вам прекратить использовать этот метод и использовать комбинацию TestCategory для создания легко выполняемых групп тестов.

Как вы можете добавить несколько параметров для тестовых контейнеров, вы можете группировать их в один вызов:

/testcontainer:[file name]        Load a file that contains tests. You can
                                  Specify this option more than once to
                                  load multiple test files.
                                  Examples:
                                    /testcontainer:mytestproject.dll
                                    /testcontainer:loadtest1.loadtest

MsTest /testcontainer:assemblyone.dll /testcontainer:assemblytwo.dll /testcontainer:assembly3.dll

Запуск MsTest на нескольких сборках сразу. И пока не используйте XUnit.NET или NUnit, так как они не могут быть объединены в один отчет, не переключаясь на новый тестовый бег Agile.

Ответ 3

Я не знаю, поможет ли это вам или нет, но я использую Invoke-MsBuild. Это модуль PowerShell, который должен делать именно то, что вам нужно. Я не знаю, искали ли вы решение PowerShell или нет, но он отлично работает!

У него также есть сестра script, Invoke-MsTest для запуска MsTest вместо MsBuild.

Ответ 4

Просто для полноты, я часто хочу запускать тесты в качестве консольного приложения, просто потому, что мне почему-то намного легче отлаживать это по какой-то причине... На протяжении многих лет я создал несколько небольших помощников по тестированию, чтобы помочь мне; Я полагаю, вы можете легко использовать их с вашим CI-решением.

Я понимаю, что это не ваш вопрос полностью; однако, поскольку вы ищете решение CI и упоминаете визуальную студию, это должно решить ее довольно хорошо.

Просто, чтобы вы знали, моя небольшая структура немного больше этого, но недостающие вещи довольно легко добавить. В основном, я оставил все для ведения журнала и тот факт, что я тестирую разные сборки в разных доменах приложений (из-за возможных конфликтов и состояний DLL). Подробнее об этом ниже.

Одна вещь, которую следует заметить, заключается в том, что я не поймаю никаких исключений в нижеприведенном процессе. Мое основное внимание уделяет легкому отладке приложения при устранении неполадок. У меня есть отдельная (но аналогичная) реализация для CI, которая в основном добавляет попытку/уловки в комментариях ниже.

В этот метод допускается только одно уловка: Visual Studio не копирует все ссылки, которые вы ссылаетесь; он копирует только сборки, которые вы используете в коде. Простым обходным путем для этого является введение метода (который никогда не вызывается), который использует один тип в тестируемой DLL. Таким образом, ваша сборка будет скопирована, и все будет хорошо работать.

Здесь код:

static class TestHelpers
{
    public static void TestAll(this object o)
    {
        foreach (MethodInfo meth in o.GetType().GetMethods().
            Where((a) => a.GetCustomAttributes(true).
                Any((b) => b.GetType().Name.Contains("TestMethod"))))
        {
            Console.WriteLine();
            Console.WriteLine("--- Testing {0} ---", meth.Name);
            Console.WriteLine();

            // Add exception handling here for your CI solution.
            var del = (Action)meth.CreateDelegate(typeof(Action), o);
            del();

            // NOTE: Don't use meth.Invoke(o, new object[0]); ! It'll eat your exception!

            Console.WriteLine();
        }
    }

    public static void TestAll(this Assembly ass)
    {
        HashSet<AssemblyName> visited = new HashSet<AssemblyName>();
        Stack<Assembly> todo = new Stack<Assembly>();
        todo.Push(ass);

        HandleStack(visited, todo);

    }

    private static void HandleStack(HashSet<AssemblyName> visited, Stack<Assembly> todo)
    {
        while (todo.Count > 0)
        {
            var assembly = todo.Pop();

            // Collect all assemblies that are related
            foreach (var refass in assembly.GetReferencedAssemblies())
            {
                TryAdd(refass, visited, todo);
            }

            foreach (var type in assembly.GetTypes().
                Where((a) => a.GetCustomAttributes(true).
                    Any((b) => b.GetType().Name.Contains("TestClass"))))
            {
                // Add exception handling here for your CI solution.
                var obj = Activator.CreateInstance(type);
                obj.TestAll();
            }
        }
    }

    public static void TestAll()
    {
        HashSet<AssemblyName> visited = new HashSet<AssemblyName>();
        Stack<Assembly> todo = new Stack<Assembly>();

        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            TryAdd(assembly.GetName(), visited, todo);
        }

        HandleStack(visited, todo);
    }

    private static void TryAdd(AssemblyName ass, HashSet<AssemblyName> visited, Stack<Assembly> todo)
    {
        try
        {
            var reference = Assembly.Load(ass);

            if (reference != null &&
                !reference.GlobalAssemblyCache &&           // Ignore GAC
                reference.FullName != null && 
                !reference.FullName.StartsWith("ms") &&     // mscorlib and other microsoft stuff
                !reference.FullName.StartsWith("vshost") && // visual studio host process
                !reference.FullName.StartsWith("System"))   // System libraries
            {
                if (visited.Add(reference.GetName()))       // We don't want to test assemblies twice
                {
                    todo.Push(reference);                   // Queue assembly for processing
                }
            }
        }
        catch
        {
            // Perhaps log something here... I currently don't because I don't care...
        }
    }
}

Как использовать этот код:

  • Вы можете просто вызвать TestHelpers.TestAll() для проверки всех сборок, ссылок на сборку, косвенно связанных сборок и т.д. Это, вероятно, то, что вы хотите сделать в CI.
  • Вы можете вызвать TestHelpers.TestAll(assembly), чтобы протестировать одну сборку со всеми ссылочными сборками. Это может быть полезно, когда вы раскалываете тесты на нескольких сборках и/или при отладке.
  • Вы можете вызвать new MyObject().TestAll() для вызова всех тестов в одном объекте. Это особенно полезно при отладке.

Если вы используете такие приложения, как я, вы должны создать единый домен для DLL, который вы динамически загружаете из папки и используете TestAll. Кроме того, если вы используете папку с царапинами, вы можете удалить ее между тестами. Таким образом, несколько версий тестовых рамок и несколько тестов не будут взаимодействовать друг с другом. В частности, если ваши тесты используют состояние (например, статические переменные), это может быть хорошей практикой. Есть несколько примеров для CreateInstanceAndUnwrap онлайн, которые помогут вам в этом.

Следует отметить, что я использую делегат вместо method.Invoke. Это в основном означает, что ваш объект исключения не будет поглощен Reflection, что означает, что ваш отладчик не будет разбит. Также обратите внимание, что я проверяю атрибуты по имени, что означает, что это будет работать с разными фреймворками - если имена атрибутов совпадают.

НТН

Ответ 5

Прочтите раздел "Предостережение" в вашей собственной ссылке. В VST 2012 он больше не поддерживается так, как вы это делаете.

Возможно, эта версия обновления может помочь: http://msdn.microsoft.com/en-us/library/ms182490.aspx