Издевательствование с помощью FileStream

Я пытаюсь издеваться над использованием файла с потоком файлов, но не смог его завершить и не знаю, как это сделать, я использую rhino mock.

private Connection LoadConnectionDetailsFromDisk(string bodyFile)
{     
   //logic before
   using (FileStream fs = File.Open(bodyFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
   {
     return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }
    //more logic
}

Может ли кто-нибудь сказать мне, как издеваться над использованием (FileStream....), поэтому я могу получить доступ к этой ветке кода?

Ответ 1

Вы должны абстрагироваться File.Open() с помощью метода интерфейса, тогда вы могли бы насмехаться над ним.

Итак,

1) Создайте интерфейс:

public interface IFileDataSource
{
   FileStream Open(string path,
                   FileMode mode,
                   FileAccess access,
                   FileShare share);
}

2) Измените LoadConnectionDetailsFromDisk() следующим образом:

private Connection LoadConnectionDetailsFromDisk(string path, IFileDataSource fileSource)
{     
   using (FileStream fs = fileSource.Open(bodyFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
   {
      return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }

   //more logic
}

3) В тесте mock интерфейс и вводит макет

// create a mock instance
var sourceMock = MockRepository.GenerateMock<IFileDataSource>();

// setup expectation
sourceMock.Expect(m => m.Open("path", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
         .CallBack(
 delegate (string path, FileMode mode, FileAccess access, FileShare share)
 {
      // handle a call

     return true;
 }).Repeat.Any();

// TODO: depends on how you are triggering LoadConnectionDetailsFromDisk method call
// inject a mock

Учитывая, что LoadConnectionDetailsFromDisk() вы не можете вводить макет непосредственно в этот вызов метода из теста, поэтому, пожалуйста, покажите, как этот метод вызывается.

Ответ 2

Вы можете использовать SystemWrapper lib для этой цели. Он содержит интерфейсы и классы, которые обертывают системные классы и позволяют вам использовать методы тестирования, используя эти классы.

Ответ 3

System.IO.Exception project и пакет NuGet также позволяют насмехаться над FileStreams.

Чтобы использовать это, вы должны немного изменить способ получения FileStream, например:

private readonly IFileSystem _fileSystem; // this is from System.IO.Exception

// This is assuming dependency injection to insert the mock file system during testing, 
// or the real one in production
public YourConstructor(IFileSystem fileSystem)
{
   _fileSystem = fileSystem;
}

private Connection LoadConnectionDetailsFromDisk(string bodyFile)
{     
   using (Stream fs = _fileSystem.FileStream.Create(bodyFile, FileMode.Open))
   {
       return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }

   //more logic
}

Ответ 4

Я бы реорганизовал метод таким образом, чтобы FileStream передавался методу, чтобы вы могли создать макет файла filestream

private Connection LoadConnectionDetailsFromDisk(FileStream bodyFile)
{ 
  ....
}

Если вы действительно хотите получить фантазию, вы всегда можете принять IStream, а затем Mock, что IStream, но Rhino поддерживает создание mocks concreate classes

 MockRepository mocks = new MockRepository();
 FileStream basket = mocks.CreateMock<FileStream>();

Ответ 5

Я справился с этим, создав подкласс FileStream. Базовый конструктор действительно требует, чтобы мы открыли фактический файл, поэтому я включаю простой текстовый файл, для которого заданы "Содержимое" и "Включить, если новее", который мы открываем только для чтения и для совместного использования.

Затем я перезаписываю метод, который мне действительно нужен. В моем случае мне нужен метод write, и мне нужно что-то делать с тем, что написано, поэтому я добавляю callback для вызова.

private class CustomFileStream : FileStream
{
    private Action<string> WriteAction { get; }
    public CustomFileStream(Action<string> writeAction)
        // FileStream is notoriously hard to mock, so we inherit from it and we are a lightweight, real (but unused) FileStream underneath
        : base(Path.Combine(new ExecutionDirectory().Path /* Handle the execution directory in your own way */, "Content/DummyTextFile.txt"), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    {
        this.WriteAction = writeAction;
    }

    public override bool CanWrite => true;
    public override void Write(ReadOnlySpan<byte> buffer)
    {
        this.WriteAction(Encoding.UTF8.GetString(buffer));
    }
}