Как MapPath в модульном тесте в C#

Я хочу загрузить внешний XML-файл в модульном тесте, чтобы протестировать некоторый код обработки этого XML. Как мне получить путь к файлу?

Обычно в веб-приложении я бы делал:

XDocument.Load(Server.MapPath("/myFile.xml"));

Но очевидно, что в моем модульном тесте у меня нет ссылок на Server или HttpContext, так как я могу сопоставить путь, чтобы мне не пришлось указывать полный путь?

ОБНОВИТЬ:

Я просто хочу прояснить, что код, который я на самом деле тестирую, предназначен для класса XML-анализатора, что-то вроде:

public static class CustomerXmlParser {
  public static Customer ParseXml(XDocument xdoc) {
    //...
  }
}

Поэтому, чтобы проверить это, мне нужно проанализировать действительный XDocument. Тестируемый метод не имеет доступа к самой файловой системе. Я мог создать XDocument из String непосредственно в тестовом коде, но я думал, что будет проще просто загрузить его из файла.

6 ответов

Решение

Другая идея заключается в использовании внедрения зависимости.

public interface IPathMapper {
string MapPath(string relativePath);
}

А потом просто использовать 2 реализации

public class ServerPathMapper : IPathMapper {
     public string MapPath(string relativePath){
          return HttpContext.Current.Server.MapPath(relativePath);
     }
}

И тогда вам также нужна ваша фиктивная реализация

public class DummyPathMapper : IPathMapper {
    public string MapPath(string relativePath){
        return "C:/Basedir/" + relativePath;
    }
}

И тогда все ваши функции, которым нужно сопоставить пути, просто должны иметь доступ к экземпляру IPathMapper - в вашем веб-приложении это должен быть ServerPathMapper, а в вашем модуле тестируется DummyPathMapper - базовый DI (Dependency Injection).

Лично я бы очень осторожно относился к любому коду, который опирается на внутреннее хранилище ресурсов, будь то файловая система или база данных - вы вводите зависимость в свой модульный тест, который может привести к ложным отрицаниям, то есть к тестам сбой не из-за вашего конкретного тестового кода, а из-за того, что файл отсутствует или сервер недоступен и т. д.
Посмотрите эту ссылку для ИМО, чтобы получить хорошее определение того, что такое юнит-тест, а главное - нет

Ваш модульный тест должен тестировать элементарный, четко определенный функционал, а не тестировать, может ли файл загружаться. Одно из решений состоит в том, чтобы "смоделировать" загрузку файла - однако существуют различные подходы к этому, однако, я бы лично смоделировал только интерфейс с используемой вами файловой системой, а не пытался делать полную насмешку над файловой системой - вот хороший пост SO и вот хорошее ТАК обсуждение насмешек над файловой системой

надеюсь, это поможет

Обычно для модульных тестов я добавляю XML-файлы в качестве встроенных ресурсов в проект и загружаю их, используя метод, подобный следующему:

public static string LoadResource(string name)
{
  Type thisType = MethodBase.GetCurrentMethod().DeclaringType;
  string fullName = thisType.Namespace + "." + name + ".xml";

  using (Stream stream = thisType.Module.Assembly.GetManifestResourceStream(fullName))
  {
      if(stream==null)
      {
        throw new ArgumentException("Resource "+name+" not found.");
      }

      StreamReader sr = new StreamReader(stream);
      return sr.ReadToEnd();
  }
}

Изменить: я начинаю с нуля, так как я думаю, что я неправильно истолковал ваш вопрос.

Лучший способ загрузить XML-файл в модульном тесте для последующего внедрения его в некоторые из ваших классов - это использовать атрибут DeploymentItem в модульных тестах MS.

Это будет выглядеть следующим образом:

[TestMethod]
[DeploymentItem(@"DataXmlFiles\MyTestFile.xml", "DataFiles")]
public void LoadXMLFileTest()
{
   //instead of "object" use your returning type (i.e. string, XDocument or whatever)
   //LoadXmlFile could be a method in the unit test that actually loads an XML file from the File system
   object myLoadedFile = LoadXmlFile(Path.Combine(TestContext.TestDeploymentDir, "DataFiles\\MyTestFile.xml"));

   //do some unit test assertions to verify the outcome
}

Я не тестировал код сейчас на отладчике, но он должен работать.

Редактировать: Кстати, когда вы используете DeploymentItem рассмотреть этот пост здесь.

Классы:

internal class FakeHttpContext : HttpContextBase
{
    public override HttpRequestBase Request { get { return new FakeHttpRequest(); } }
}

internal class FakeHttpRequest : HttpRequestBase
{
    public override string MapPath(string virtualPath)
    {
        return /* your mock */
    }
}

Использование:

[TestMethod]
public void TestMethod()
{
    var context = new FakeHttpContext();
    string pathToFile = context.Request.MapPath("~/static/all.js");
}

Это может быть полезно для кого-то. У меня была связанная проблема. Хотел использовать файл Excel из папки корневого уровня в моем проекте C# Unit Test.

У меня была корневая папка с именем "TestFiles". Внутри у меня был "Test.xlsx".

То, что я сделал, было:

Щелкните правой кнопкой мыши файл "Test.xlsx", перейдите в "Свойства" и установите "Копировать в выходной каталог" = "Копировать всегда"

Теперь файл и содержащая его папка "TestFiles" всегда копируются в папку bin проекта Unit Test. Так что я смог использовать это так:

var filePath = "TestFiles/Test.xlsx";
var strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=0\"";
using (var conn = new OleDbConnection(strConn))
{
                conn.Open();
...
}
Другие вопросы по тегам