#c# #.net #unit-testing #nunit
#c# #.net #модульное тестирование #nunit
Вопрос:
Вот моя проблема с разработкой модульного теста на c #:
У меня есть один тест, который я хочу запустить для некоторого кода, который изменяет файл. Мой тест запустит код, который изменяет файл, а затем проверит результат. Пока довольно прямолинейно…
Проблема в том, что у меня есть около 10 файлов (скоро будет намного больше), с которыми я хочу выполнить тот же модульный тест. Я не хочу писать новый модульный тест для каждого файла, когда сам тест действительно один и тот же.
Я мог бы написать один тест, который запрашивает файлы в папке, а затем запускает логику тестирования для каждого файла, но при использовании этого метода будет сообщаться только один результат прохождения / сбоя для всего набора файлов.
Я хотел бы создать какую-то динамическую систему, в которой каждый файл, который я помещаю в определенную папку, запускается через этот же модульный тест. Итак, если в папке находится 25 файлов, тест выполняется 25 раз, и результаты модульного теста сообщают, что было выполнено 25 тестов, и включают отдельный проход / сбой для каждого.
Есть идеи, как или если это можно сделать в модульном тестировании c #? Или с помощью теста nunit?
Спасибо! И я надеюсь, что это не дублирующий вопрос. Я огляделся, но ничего не смог найти.
Комментарии:
1. Некоторые утверждают, что тест, который включает ввод-вывод (в данном случае манипулирование файловой системой), не является модульным тестом. Альтернативный подход заключается в представлении операций ввода-вывода в виде вводимых зависимостей. Однако большая часть системы. Типы ввода-вывода (и, конечно же, статика) не подходят для этого. Подход, который я использовал, заключается в создании прокси-сборок с тривиальными типами пересылки методов и соответствующими интерфейсами. Это позволяет вам оставить этот тонкий прокси-уровень непроверенным, а ваш логический уровень полностью протестирован. Microsoft Moles эффективно выполняет это динамически, но это все еще WIP.
Ответ №1:
Похоже, вам нужен параметризованный тестовый пример: см. http://www.nunit.org/index.php?p=testCaseSourceamp;r=2.5 .
Итак:
[TestFixture]
public class Tests
{
static string[] FileNames = new string[]
{ "mary.txt", "mungo.txt", "midge.txt" };
[Test, TestCaseSource("FileNames")]
public void TestMethod(string fileName)
{
Assert.That(File.Exists(fileName));
}
}
[TestCaseSource]
Атрибут сообщает NUnit получить значения строкового параметра из массива строк.
Однако этот подход требует статических констант для имен файлов. Если вы предпочитаете программный подход, который считывает файлы из базы данных или тому подобное, попробуйте фабричный класс, например:
[TestFixture]
public class Tests
{
[Test, TestCaseSource(typeof(FilenameFactory), "FileNames")]
public bool FileCheck(string fileName)
{
return File.Exists(fileName);
}
}
public class FilenameFactory
{
public static IEnumerable FileNames
{
get
{
foreach (var filename in
Directory.EnumerateFiles(Environment.CurrentDirectory))
{
yield return new TestCaseData(filename).Returns(true);
}
}
}
}
TestCaseData
Класс генерирует «тестовые примеры», которые имеют ожидания, которые можно задать через свободный интерфейс.
Комментарии:
1. Это тоже действительно отличное решение. Спасибо, Джереми.
Ответ №2:
Параметризованные тесты ( http://www.nunit.org/index.php?p=parameterizedTestsamp;r=2.5 ) могут быть вам полезны в зависимости от того, как вы их выполняете.
Пример TestcaseAttribute
, который вы можете использовать, или есть также TestCaseSourceAttribute
[TestCase(12,3,4)]
[TestCase(12,2,6)]
[TestCase(12,4,3)]
public void DivideTest(int n, int d, int q)
{
Assert.AreEqual( q, n / d );
}
Вы получаете преимущество написания и поддержки одного теста, но результаты для каждого случая.
Ответ №3:
Другой вариант — использовать шаблоны T4 для динамической генерации тестов для вас на основе количества файлов в вашем каталоге. Добавьте этот файл «.tt» в свой проект модульного тестирования.
Теперь всякий раз, когда вы выполняете сборку, которая должна произойти непосредственно перед нажатием кнопки «Запустить все тесты в решении» в Visual Studio, она должна генерировать модульные тесты для всех файлов в каталоге с именем файла в модульном тесте. Тогда в вашем тестовом запуске должен быть хороший список всех проверенных файлов и их статусов.
<#@ template debug="false" hostspecific="true" language="C#v3.5" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.dll" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ output extension=".cs" #>
<#
string inputDirectory = @"d:temp";
var files = System.IO.Directory.GetFiles(inputDirectory);
#>
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestProject1
{
[TestClass]
public class UnitTest1
{
<# foreach (string filePath in files) { #>
[TestMethod]
public void TestingFile_<#=System.IO.Path.GetFileNameWithoutExtension(filePath).Replace(' ','_').Replace('-','_')#>()
{
File currentFile = System.IO.File.Open(@"<#= filePath #>", FileMode.Open);
// TODO: Put your standard test code here which will use the file you opened above
}
<# } #>
}
}
Комментарии:
1. Это отличная вещь! Я до сих пор не знал об этой функции, поэтому я очень рад, что наконец-то задал вопрос о stackoverflow! 🙂
2. Спасибо всем за отличный вклад в мой вопрос!
Ответ №4:
Прежде всего, зависимость теста от внешних источников, таких как файлы на диске, не является строго модульным тестированием (мнение).
То, что вы ищете, — это подход к тестированию, основанный на данных, а затем вам следует искать инструменты, которые его поддерживают, такие как MSTest или MbUnit. (Перейдите по ссылкам для получения дополнительной информации).
Затем вы можете сделать что-то вроде этого (используя MbUnit):
[TestFixture]
public class MyTestFixture
{
[Test]
public void WordCounter([TextData(ResourcePath = "Data.txt")] string text)
{
var wordCounter = new WordCounter(text);
int count = wordCounter.Count("Firecrest");
Assert.AreEqual(4, count); // Should not include the plural form "Firecrests".
}
}