Поддельный поток для запечатанного PngBitmapEncoder - PullRequest
0 голосов
/ 26 апреля 2018

Это дополнительный вопрос к Модульное тестирование с зависимостями ввода / вывода .

Как мне заставить PngBitmapEncoder принять упакованный / поддельный FileStream?

В BitmapService.SaveBitmapAsPngImage () я хочу утверждать, что битовый кодировщик вызывает потоковое сохранение bitmapEncoder.Save (outStream.StreamInstance);

Пробный тест Rhino, для которого требуется "действительный" FileStream для PngBitmapEncoder:

[Test]
public void BitmapService_Should_SaveBitmapAsPngImage_RhinoMocks()
{
    // Arrange
    IFile fileMock = MockRepository.GenerateStrictMock<IFile>();
    IFileStream fileStreamWrapperMock = MockRepository.GenerateStub<IFileStream>();
    fileMock.Expect(x => x.Open(string.Empty, FileMode.OpenOrCreate))
        .IgnoreArguments().Return(fileStreamWrapperMock);
    var bitmapEncoderFactory = MockRepository.GenerateStub<IBitmapEncoderFactory>();

    PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
    bitmapEncoderFactory.Expect(x => x.CreateBitmapEncoder()).Return(pngBitmapEncoder);

    BitmapService sut = new BitmapService(fileMock, new PngBitmapEncoderFactory());
    Size renderSize = new Size(100, 50);
    RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
        (int)renderSize.Width, (int)renderSize.Height, 96d, 96d, PixelFormats.Pbgra32);

    // Act
    sut.SaveBitmapAsPngImage(new Uri("//A_valid_path"), renderBitmap);

    // Assert
    pngBitmapEncoder.AssertWasCalled(x => x.Save(fileStreamWrapperMock.FileStreamInstance));
}

Результат теста Rhino:

System.ArgumentNullException: значение не может быть нулевым. Имя параметра: поток

в System.Windows.Media.StreamAsIStream.IStreamFrom (потоковый поток)

в System.Windows.Media.Imaging.BitmapEncoder.Save (Поток)

at BitmapService.SaveBitmapAsPngImage (путь Uri, BitmapSource renderBitmap)

Я предпочитаю макеты Rhino, но вот тест Moq от @ Nkosi . К сожалению, это тоже не работает:

[TestMethod]
public void BitmapService_Should_SaveBitmapAsPngImage()
{
    //Arrange
    var mockedStream = Mock.Of<Stream>(_ => _.CanRead == true && _.CanWrite == true);
    Mock.Get(mockedStream).SetupAllProperties();
    var fileSystemMock = new Mock<IFileSystem>();
    fileSystemMock
        .Setup(_ => _.OpenOrCreateFileStream(It.IsAny<string>()))
        .Returns(mockedStream);

    var sut = new BitmapService(fileSystemMock.Object);
    Size renderSize = new Size(100, 50);
    var renderBitmap = new RenderTargetBitmap(
        (int)renderSize.Width, (int)renderSize.Height, 96d, 96d, PixelFormats.Pbgra32);
    var path = new Uri("//A_valid_path");

    //Act
    sut.SaveBitmapAsPngImage(path, renderBitmap);

    //Assert
    Mock.Get(mockedStream)
        .Verify(_ => _.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()));
}

Результат теста Moq:

BitmapServiceTest.BitmapService_Should_SaveBitmapAsPngImage threw исключение: System.IO.IOException: невозможно прочитать из потока. ---> System.Runtime.InteropServices.COMException: исключение из HRESULT: 0x88982F72 at System.Windows.Media.Imaging.BitmapEncoder.Save (Поток)

Итак, тест пытается использовать этот поток.


Тестируемый класс:

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SystemInterface.IO;
using SystemWrapper.IO;

public interface IBitmapService
{
    void SaveBitmapAsPngImage(Uri path, BitmapSource renderBitmap);
}

public interface IBitmapEncoderFactory
{
    BitmapEncoder CreateBitmapEncoder();
}

public class PngBitmapEncoderFactory : IBitmapEncoderFactory
{
    public BitmapEncoder CreateBitmapEncoder()
    {
        return new PngBitmapEncoder();
    }
}

public class BitmapService : IBitmapService
{
    private readonly IFile _fileWrapper;
    private readonly IBitmapEncoderFactory _bitmapEncoderFactory;

    public BitmapService(IFile fileWrapper, IBitmapEncoderFactory bitmapEncoderFactory)
    {
        _fileWrapper = fileWrapper;
        _bitmapEncoderFactory = bitmapEncoderFactory;
    }

    public void SaveBitmapAsPngImage(Uri path, BitmapSource renderBitmap)
    {
        // Create a file stream for saving image
        using (IStream outStream = _fileWrapper
            .Open(path.LocalPath, FileMode.OpenOrCreate))
        {
            // Use bitmap encoder for our data
            BitmapEncoder bitmapEncoder = _bitmapEncoderFactory.CreateBitmapEncoder();
            // push the rendered bitmap to it
            bitmapEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));

            // The problem: Expects real Stream as parameter!!!
            bitmapEncoder.Save(outStream.StreamInstance);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Большое спасибо @NKosi за руководство в правильном направлении. Для полноты картины я хочу опубликовать тест NUnit, Rhino mock.

[Test]
public void ShouldSaveBitmapAsPngImage()
{
    // Arrange
    Uri pathToFile = new Uri("//A_valid_path");
    IFileSystem fileSystemMock = MockRepository.GenerateStrictMock<IFileSystem>();
    MockedFileStream mockedFileStream = new MockedFileStream();
    fileSystemMock.Expect(x => x.OpenOrCreateFileStream(pathToFile.AbsolutePath))
        .IgnoreArguments().Return(mockedFileStream);

    BitmapService sut = new BitmapService(fileSystemMock);
    Size renderSize = new Size(100, 50);
    RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
        (int)renderSize.Width, (int)renderSize.Height, 96d, 96d, PixelFormats.Pbgra32);

    // Act
    sut.SaveBitmapAsPngImage(pathToFile, renderBitmap);

    // Assert
    // Was data was written to it?
    Assert.That(mockedFileStream.Length, Is.GreaterThan(0));

    mockedFileStream.CustomDispose(); //done with stream
}

И избавился от абстракции BitmapEncoder от сервиса:

public class BitmapService : IBitmapService
{
    private readonly IFileSystem _fileSystem;

    public BitmapService(IFileSystem fileSystem)
    {
        _fileSystem = fileSystem;
    }

    public void SaveBitmapAsPngImage(Uri path, BitmapSource renderBitmap)
    {
        // Create a file stream for saving image
        using (Stream outStream = _fileSystem.OpenOrCreateFileStream(path.LocalPath))
        {
            // Use bitmap encoder for our data
            BitmapEncoder bitmapEncoder = new PngBitmapEncoder();
            // push the rendered bitmap to it
            bitmapEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
            // save the data to the stream
            bitmapEncoder.Save(outStream);
        }
    }
}

Я действительно доволен аккуратным и чистым решением. Спасибо @NKosi и StackOverflow;)

0 голосов
/ 26 апреля 2018

Как я и предлагал в комментариях, если насмешка над Stream не работает для вас с фреймворком, рассмотрите возможность создания насмешки / заглушки, обернув MemoryStream,

public class MockedFileStream : MemoryStream {
    protected override void Dispose(bool disposing) {
        //base.Dispose(disposing);
        //No Op fr the purposes of the test.
    }

    public override void Close() {
        //base.Close();
        //No Op fr the purposes of the test.
    }

    public void CustomDispose() {
        base.Dispose(true);
        GC.SuppressFinalize(this);
    }
}

что бы все сантехника уже там для вас. Единственный улов в том, что вам придется переопределить метод Dispose, поскольку поток используется с оператором using.

Затем тест будет обновлен для использования «фальшивого» * ​​1011 * потока

[TestMethod]
public void BitmapService_Should_SaveBitmapAsPngImage() {
    //Arrange
    var mockedStream = new MockedFileStream();
    var fileSystemMock = new Mock<ImageDrawingCombiner3.IFileSystem>();
    fileSystemMock
        .Setup(_ => _.OpenOrCreateFileStream(It.IsAny<string>()))
        .Returns(mockedStream);

    var sut = new ImageDrawingCombiner3.BitmapService(fileSystemMock.Object);
    Size renderSize = new Size(100, 50);
    var renderBitmap = new RenderTargetBitmap(
        (int)renderSize.Width, (int)renderSize.Height, 96d, 96d, PixelFormats.Pbgra32);
    var path = new Uri("//A_valid_path");

    //Act
    sut.SaveBitmapAsPngImage(path, renderBitmap);

    //Assert
    mockedStream.Length.Should().BeGreaterThan(0); //data was written to it.

    mockedStream.CustomDispose(); //done with stream
}

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

...