【问题标题】:Unit testing file upload fails单元测试文件上传失败
【发布时间】:2014-07-19 03:42:18
【问题描述】:

我有一个在数据库中插入文件的操作。它工作正常。但是插入成功时单元测试失败。 RedirectToRouteResult 为空。这是操作的代码:

[HttpPost] public ActionResult FileUpload(FileUploadViewModel model) { try { if (ModelState.IsValid) { var uploadFile = new byte[model.File.InputStream.Length]; model.File.InputStream.Read(uploadFile, 0, uploadFile.Length); //var file = new FilesUpload(); var fileExt = Path.GetExtension(model.File.FileName); if (fileExt == ".txt" || fileExt == ".docx") { //file.FileName = model.File.FileName; //file.FileSize = model.File.ContentLength; //file.ContentType = fileExt; //file.FileData = uploadFile; //file.UploadedOn = DateTime.Now; //var user = _userRepository.GetUserId(User.Identity.Name); //file.UserId = user.UserId; bool result = _userRepository.UploadFile(model, User.Identity.Name, fileExt, uploadFile); //file.User = (from u in _context.Users where u.UserId == 29 select u).FirstOrDefault(); //_context.FilesUploads.Add(file); //int result = _context.SaveChanges(); if (result) { return RedirectToAction("Index"); } else { ModelState.AddModelError("", "An error occured"); return View(model); } } else { ModelState.AddModelError("", "You can upload only files with extensions .txt and .docx"); } } return View(model); } catch (Exception ex) { throw(ex); } }

这是单元测试的代码:

    private SuperUserController _controller;
    private Mock<IUserRepository> _repositoryMock;
    private Mock<IIntrinsicObjects> _intrinsicMock;
    private Mock<ControllerContext> _contextMock;
    private string username = "test@abv.bg";
    private FileUploadViewModel _fileUpload;

    [SetUp]
    public void Setup()
    {
        _fileUpload = new FileUploadViewModel();
        _repositoryMock = new Mock<IUserRepository>();
        _intrinsicMock = new Mock<IIntrinsicObjects>();
        _contextMock = new Mock<ControllerContext>();
        _contextMock.SetupGet(c => c.HttpContext.User.Identity.Name).Returns(username);
        _contextMock.SetupGet(c => c.HttpContext.User.Identity.IsAuthenticated).Returns(true);
        _controller = new SuperUserController(_repositoryMock.Object, _intrinsicMock.Object);
        _controller.ControllerContext = _contextMock.Object;
    }


[Test]
    public void FileUpload_ShouldRedirectToIndexOnSuccess()
    {
        string filePath = Path.GetFullPath(@"C:\Users\Test\Desktop\test.txt");
        var fileStream = new FileStream(filePath, FileMode.Open);
        var fileExt = Path.GetExtension(filePath);
        var fileToUpload = new Mock<HttpPostedFileBase>();
        fileToUpload.Setup(f => f.ContentLength).Returns(10);
        fileToUpload.Setup(f => f.FileName).Returns("test.txt");
        fileToUpload.Setup(f => f.ContentType).Returns(fileExt);
        fileToUpload.Setup(f => f.InputStream).Returns(fileStream);
        //var model = new FileUploadViewModel();
        _fileUpload.File = fileToUpload.Object;
        var fileData = new byte[fileToUpload.Object.InputStream.Length];
        _repositoryMock.Setup(m => m.UploadFile(_fileUpload, _controller.ControllerContext.HttpContext.User.Identity.Name, fileToUpload.Object.ContentType, fileData)).Returns(true);
        var result = _controller.FileUpload(_fileUpload) as RedirectToRouteResult;
        Assert.IsNotNull(result);
    }

问题是 UploadFile 总是返回 false。

如果有人帮助我,我将非常感激。

【问题讨论】:

    标签: asp.net-mvc unit-testing


    【解决方案1】:

    您的控制器操作使用文件数据创建一个新数组:

    var uploadFile = new byte[model.File.InputStream.Length];
    

    这永远不会匹配您的_repositoryMockUploadFile 上设置的fileData 参数:

    _repositoryMock.Setup(m => m.UploadFile(_fileUpload, _controller.ControllerContext.HttpContext.User.Identity.Name, fileToUpload.Object.ContentType, fileData)).Returns(true);
    

    默认情况下,数组将通过引用匹配,而不是在内容相同的情况下。当控制器创建一个新实例时,uploadFilefileData 的引用不同。

    您想设置最小起订量,以便在提交预期的数据数组时,您的 UploadFile 函数返回 true。为此,您可以使用自定义匹配器来匹配可枚举中的所有元素:

    public T[] MatchCollection<T>(T[] expectation)
    {
        //This checks all elements in input are find in destination, regardless of order or duplicates
        return Match.Create<T[]>(inputCollection => (expectation.All((i) => inputCollection.Contains(i))));
    
        //This will check the arrays are exactly the same
        return Match.Create<T[]>(inputCollection =>  StructuralComparisons.StructuralEqualityComparer.Equals(inputCollection, expectation) );
    }
    

    并使用该匹配器为数据数组设置模拟:

    _repositoryMock.Setup(m => m.UploadFile(_fileUpload, _controller.ControllerContext.HttpContext.User.Identity.Name, fileToUpload.Object.ContentType, MatchCollection(fileData))).Returns(true);
    

    希望对你有帮助!

    编辑


    我根据您的代码尝试了以下测试,该代码将有效的数组数据传递给模拟设置,并且不需要在单元测试中实际读取文件:

    public void FileUpload_ShouldRedirectToIndexOnSuccess()
    {
        var fileData = UTF8Encoding.UTF8.GetBytes("This is a test");
        var fileStream = new MemoryStream(fileData);
    
        var fileToUpload = new Mock<HttpPostedFileBase>();
        fileToUpload.Setup(f => f.ContentLength).Returns((int)fileStream.Length);
        fileToUpload.Setup(f => f.FileName).Returns("test.txt");
        fileToUpload.Setup(f => f.ContentType).Returns("txt");
        fileToUpload.Setup(f => f.InputStream).Returns(fileStream);
        _fileUpload.File = fileToUpload.Object;            
    
        _repositoryMock
            .Setup(m => m.UploadFile(_fileUpload, username, ".txt", MatchCollection(fileData)))
            .Returns(true);
    
        var result = _controller.FileUpload(_fileUpload) as RedirectToRouteResult;
        Assert.IsNotNull(result);
    }
    

    【讨论】:

    • 你能告诉我我必须把这个匹配器放在哪里吗?
    • 它给了我一个错误“无法从 'System.Collections.Generic.IEnumerable' 转换为 'byte[]'”
    • 对不起,匹配器的返回类型应该和参数输入的一样,所以你需要一个匹配器T[]而不是IEnumerable&lt;T&gt;。我已经更新了答案。
    • 它仍然给出错误。知道为什么吗?
    • 我发现测试中创建的数组只包含零,而控制器中的数组包含不同于零的值。也许这就是它不起作用的原因。
    【解决方案2】:

    这是测试的最终版本:

    [Test]
        public void FileUpload_ShouldRedirectToIndexOnSuccess()
        {
            string filePath = Path.GetFullPath(@"C:\Users\Yordanka\Desktop\test.txt");
            var fileStream = new FileStream(filePath, FileMode.Open);
            var fileExt = Path.GetExtension(filePath);
            var testarray = new byte[414];
            var fileToUpload = new Mock<HttpPostedFileBase>();
            fileToUpload.Setup(f => f.ContentLength).Returns(414);
            fileToUpload.Setup(f => f.FileName).Returns("test.txt");
            fileToUpload.Setup(f => f.ContentType).Returns(fileExt);
            fileToUpload.Setup(f => f.InputStream).Returns(fileStream);
            fileToUpload.Setup(f => f.InputStream.Length).Returns(testarray.Length);
            _fileUpload.File = fileToUpload.Object;
            var fileData = new byte[414];
            _fileUpload.File.InputStream.Read(fileData, 0, fileData.Length);
            _repositoryMock.Setup(
                m =>
                    m.UploadFile(_fileUpload, _controller.ControllerContext.HttpContext.User.Identity.Name,
                        fileToUpload.Object.ContentType, MatchCollection(fileData))).Returns(true);
            var result = _controller.FileUpload(_fileUpload) as RedirectToRouteResult;
            Assert.IsNotNull(result);
            Assert.AreEqual("Index", result.RouteValues["Action"]);
            Assert.AreEqual("SuperUser", result.RouteValues["Controller"]);
    
        }
    

    再次感谢 Daniel J.G.!!!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-22
      • 1970-01-01
      • 2013-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多