Mocking HttpPostedFileBase and InputStream for unit-test
Asked Answered
P

3

24

I want to test the following line of code:

...
Bitmap uploadedPicture = Bitmap.FromStream(model.Picture.InputStream) as Bitmap;
...

Picture is a property in my model type HttpPostedFileBase. So I would like to mock a HttpPostedFileBase property for unit-testing:

model.Picture = new Mock<HttpPostedFileBase>().Object;

No problem at all.

Now I have to mock the InputStream, otherwise it's null:

model.Picture.InputStream = new Mock<Stream>().Object;

This isn't working as the InputStream is read-only (hasn't a setter method):

public virtual Stream InputStream { get; }

Is there a good and clean way to handle this problem? One solution would be to override HttpPostedFileBase in a derived class for my unit-test. Any other idea?

Pasteup answered 25/3, 2013 at 18:31 Comment(0)
C
36

Hi there :) I did something like,

    [TestInitialize]
    public void SetUp()
    {
        _stream = new FileStream(string.Format(
                        ConfigurationManager.AppSettings["File"],
                        AppDomain.CurrentDomain.BaseDirectory), 
                     FileMode.Open);

        // Other stuff
    }

And on the Test itself,

    [TestMethod]
    public void FileUploadTest() 
    {
        // Other stuff

        #region Mock HttpPostedFileBase

        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var files = new Mock<HttpFileCollectionBase>();
        var file = new Mock<HttpPostedFileBase>();
        context.Setup(x => x.Request).Returns(request.Object);

        files.Setup(x => x.Count).Returns(1);

        // The required properties from my Controller side
        file.Setup(x => x.InputStream).Returns(_stream);
        file.Setup(x => x.ContentLength).Returns((int)_stream.Length);
        file.Setup(x => x.FileName).Returns(_stream.Name);

        files.Setup(x => x.Get(0).InputStream).Returns(file.Object.InputStream);
        request.Setup(x => x.Files).Returns(files.Object);
        request.Setup(x => x.Files[0]).Returns(file.Object);

        _controller.ControllerContext = new ControllerContext(
                                 context.Object, new RouteData(), _controller);

        // The rest...
    }

Hope this can provide an idea to your solution :)

Crossbones answered 17/4, 2013 at 14:39 Comment(1)
How we can check the file type? FileName.EndsWith(.jpg).Returns(true) ?Jilleen
R
17

I've just been working on something similar and wanted to add the following to @TiagoC13's answer.

My system under test was a file service I'm writing, one of the requirements being to test that a file has the correct dimensions. Note, the hard-coded filename. This exists as a folder and file in my Test Project. The file's properties are as follows: Build Action : Embedded Resource and Copy to Output Directory: Copy if newer (although Copy Always should work OK)

When the project is built the testimage.jpg and its folder are added to the bin where the test then finds it.

Also note the fileStream.Close(); this releases the file so you can have a number of similar tests in the same suite.

Hope this is of help.

using Moq;
using NUnit.Framework;
using System.Web;

    [Test]
    public void IsValidFile() {
        string filePath = Path.GetFullPath(@"testfiles\testimage.jpg");
        FileStream fileStream = new FileStream(filePath, FileMode.Open);
        Mock<HttpPostedFileBase> uploadedFile = new Mock<HttpPostedFileBase>();

        uploadedFile
            .Setup(f => f.ContentLength)
            .Returns(10);

        uploadedFile
            .Setup(f => f.FileName)
            .Returns("testimage.jpg");

        uploadedFile
            .Setup(f => f.InputStream)
            .Returns(fileStream);

        var actual = fileSystemService.IsValidImage(uploadedFile.Object, 720, 960);

        Assert.That(actual, Is.True);

        fileStream.Close();
    }
Radiosensitive answered 24/4, 2014 at 15:56 Comment(0)
I
9

There is no need to create a stream from opening a file on disk. Actually I think that's a pretty horrible solution. A working test stream can be created easily enough in-memory.

var postedFile = new Mock<HttpPostedFileBase>();

using (var stream = new MemoryStream())
using (var bmp = new Bitmap(1, 1))
{
    var graphics = Graphics.FromImage(bmp);
    graphics.FillRectangle(Brushes.Black, 0, 0, 1, 1);
    bmp.Save(stream, ImageFormat.Jpeg);

    postedFile.Setup(pf => pf.InputStream).Returns(stream);

    // Assert something with postedFile here   
}        
Initial answered 18/8, 2014 at 15:38 Comment(4)
This becomes a unit test instead of a integration test as Daniel's solution. It's nice, I didn't know you could pull this one out like this, but this one is less readable (although I agree you could achieve code reuse in your tests). Not sure what's so horrible about the other one apart from having to have an image in the test project. It's not worse than having a database for integration tests...Charley
very nice @fearofawhackplanet!Fishy
@FabioMilheiro it's horrible because the OP is specifically asking about unit testing, and a unit test should not have dependency on the physical file system.Initial
@Initial Fair point. Your solution is still fine but what the OP asks and needs may not always be the same. If the image is in the test project, you can treat it as soft dependency. A database or a webservice is a whole different story. Having no external dependencies to the project means that you're just as safe.Charley

© 2022 - 2024 — McMap. All rights reserved.