Как передать ресурс решения как объект FileStream (перехваченной команде File.Open)? - PullRequest
1 голос
/ 12 августа 2010

Я расширяю устаревшую библиотеку, которая обращается к файлам на жестком диске. У меня есть такие файлы, как встроенные ресурсы в моем проекте модульного тестирования.

Я перенес части библиотеки для приема потоков, что позволяет мне использовать GetManifestResourceStream для передачи моего встроенного ресурса в устаревшую библиотеку. Это прекрасно работает, но это немного хлопот. И люди, которые поддерживают эти библиотеки, не ценят «беспорядок» или необходимость публиковать новые версии.

JustMock и TypeMock позволяют мне перехватывать команду File.Open, и я хочу передать библиотеке объект FileStream, но как мне создать объект FileStream из ресурса Embedded Manifest? Конечно, я мог бы создать физический файл, но я не хочу прикасаться к файловой системе во время выполнения тестов.

Ответы [ 3 ]

2 голосов
/ 16 августа 2010

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

           byte[] actual = new byte[255];

        // writing locally, can be done from resource manifest as well.

        using (StreamWriter writer = new StreamWriter(new MemoryStream(actual)))
        {
            writer.WriteLine("Hello world");
            writer.Flush();
        }

        // arrange the file system.

        FileStream fs = (FileStream)FormatterServices
            .GetSafeUninitializedObject(typeof(FileStream));

        // mocking the specific call and setting up expectations.
        Mock.Arrange(() => fs.Write(Arg.IsAny<byte[]>(), Arg.AnyInt, Arg.AnyInt))
            .DoInstead((byte[] content, int offset, int len) =>
        {
            actual.CopyTo(content, offset);
        });

        // return custom filestream for File.Open.
        Mock.Arrange(() => File.Open(Arg.AnyString, Arg.IsAny<FileMode>()))
             .Returns(fs);


        // act
        var fileStream =  File.Open("hello.txt", FileMode.Open);
        byte[] fakeContent = new byte[actual.Length];

        // original task
        fileStream.Write(fakeContent, 0, actual.Length);

        // assert
        Assert.Equal(fakeContent.Length, actual.Length);

        for (var i = 0; i < fakeContent.Length; i++)
        {
            Assert.Equal(fakeContent[i], actual[i]);
        }

Поскольку я шучу над членом mscorlib, а FileStream.Write является вызовом экземпляра / не содержится в наборе по умолчанию File, DateTimeFileInfo.Я также добавил следующую строку во время TestInitailization.

           Mock.Partial<FileStream>()
              .For<byte[], int, int>((x, content, offset, len) => 
            x.Write(content, offset, len));

[Отказ от ответственности, я работаю для telerik]

Надеюсь, что помогает,

Мехфуз

1 голос
/ 16 августа 2010

Вы можете прочитать следующие статьи «Чтение встроенного файла из сборки» и «C #: использование встроенного ресурса» , в котором показано, как достичь этой цели.

0 голосов
/ 23 августа 2010

Вы можете создать новый класс, производный от FileStream, возможно, с именем FauxFileStream или чем-то подобным, и переопределить все необходимые Stream связанные с ним методы для перехода в другой поток. Затем вы можете легко создать экземпляр new FauxFileStream(myManifestResourceStream) и передать его в библиотеку.

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

class FauxFileStream : FileStream
{
    private Stream _underlying;

    public FauxFileStream(Stream underlying)
    {
        _underlying = underlying;
    }

    public override bool CanRead { get { return _underlying.CanRead; } }
    public override bool CanSeek { get { return _underlying.CanSeek; } }
    public override bool CanTimeout { get { return _underlying.CanTimeout; } }
    public override bool CanWrite { get { return _underlying.CanWrite; } }
    public override long Length { get { return _underlying.Length; } }
    public override long Position { get { return _underlying.Position; } set { _underlying.Position = value; } }
    public override int ReadTimeout { get { return _underlying.ReadTimeout; } set { _underlying.ReadTimeout = value; } }
    public override int WriteTimeout { get { return _underlying.WriteTimeout; } set { _underlying.WriteTimeout = value; } }
    public override void Close() { _underlying.Close(); }
    public override void Flush() { _underlying.Flush(); }
    public override int Read(byte[] buffer, int offset, int count) { return _underlying.Read(buffer, offset, count); }
    public override int ReadByte() { return _underlying.ReadByte(); }
    public override long Seek(long offset, SeekOrigin origin) { return _underlying.Seek(offset, origin); }
    public override void SetLength(long value) { _underlying.SetLength(value); }
    public override void Write(byte[] buffer, int offset, int count) { _underlying.Write(buffer, offset, count); }
    public override void WriteByte(byte value) { _underlying.WriteByte(value); }
}

Однако на данный момент это не компилируется, потому что вам нужно вызвать базовый конструктор FileStream с некоторыми разумными аргументами. Лучшее, о чем я мог подумать, - передать что-то, что открывает какой-то файл для чтения (что будет безвредно, потому что файл на самом деле не будет прочитан, потому что все методы переопределены). Вы можете создать 0-байтовый файл для этой цели, и вы можете безопасно открыть один и тот же файл для чтения одновременно, если вы передадите FileShare.Read в качестве параметра share.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...