Search code examples
unit-testingasp.net-mvc-4asp.net-optimizationbundling-and-minification

Is it possible to unit test BundleConfig in MVC4?


As far as I can tell, the answer is no. The issue I'm seeing comes from the Include(params string[]) method in the System.Web.Optimization.Bundle class. Internally this invokes System.Web.Optimization.IncludeDirectory(string, string, bool), which in turn uses this code:

DirectoryInfo directoryInfo = new DirectoryInfo(
    HttpContext.Current.Server.MapPath(directoryVirtualPath));

While it is possible to set HttpContext.Current during a unit test, I can't figure out how to make its .Server.MapPath(string directoryVirtualPath) return a non-null string. Since the DirectoryInfo(string) constructor throws an exception when passed a null argument, such a test will always fail.

What is the .NET team's recommendation for this? Do we have to unit test bundling configurations as part of integration tests or user acceptance tests?


Solution

  • I have some good news for you, for RTM we added a new static property on BundleTable to enable more unit tests:

    public static Func<string, string> MapPathMethod;
    

    Edit Updated with a test virtual path provider:

    So you can do something like this:

    public class TestVirtualPathProvider : VirtualPathProvider {
    
        private string NormalizeVirtualPath(string virtualPath, bool isDirectory = false) {
            if (!virtualPath.StartsWith("~")) {
                virtualPath = "~" + virtualPath;
            }
            virtualPath = virtualPath.Replace('\\', '/');
            // Normalize directories to always have an ending "/"
            if (isDirectory && !virtualPath.EndsWith("/")) {
                return virtualPath + "/";
            }
            return virtualPath;
        }
    
        // Files on disk (virtualPath -> file)
        private Dictionary<string, VirtualFile> _fileMap = new Dictionary<string, VirtualFile>();
        private Dictionary<string, VirtualFile> FileMap {
            get { return _fileMap; }
        }
    
        public void AddFile(VirtualFile file) {
            FileMap[NormalizeVirtualPath(file.VirtualPath)] = file;
        }
    
        private Dictionary<string, VirtualDirectory> _directoryMap = new Dictionary<string, VirtualDirectory>();
        private Dictionary<string, VirtualDirectory> DirectoryMap {
            get { return _directoryMap; }
        }
    
        public void AddDirectory(VirtualDirectory dir) {
            DirectoryMap[NormalizeVirtualPath(dir.VirtualPath, isDirectory: true)] = dir;
        }
    
        public override bool FileExists(string virtualPath) {
            return FileMap.ContainsKey(NormalizeVirtualPath(virtualPath));
        }
    
        public override bool DirectoryExists(string virtualDir) {
            return DirectoryMap.ContainsKey(NormalizeVirtualPath(virtualDir, isDirectory: true));
        }
    
        public override VirtualFile GetFile(string virtualPath) {
            return FileMap[NormalizeVirtualPath(virtualPath)];
        }
    
        public override VirtualDirectory GetDirectory(string virtualDir) {
            return DirectoryMap[NormalizeVirtualPath(virtualDir, isDirectory: true)];
        }
    
        internal class TestVirtualFile : VirtualFile {
            public TestVirtualFile(string virtualPath, string contents)
                : base(virtualPath) {
                Contents = contents;
            }
    
            public string Contents { get; set; }
    
            public override Stream Open() {
                return new MemoryStream(UTF8Encoding.Default.GetBytes(Contents));
            }
        }
    
        internal class TestVirtualDirectory : VirtualDirectory {
            public TestVirtualDirectory(string virtualPath)
                : base(virtualPath) {
            }
    
            public List<VirtualFile> _directoryFiles = new List<VirtualFile>();
            public List<VirtualFile> DirectoryFiles {
                get {
                    return _directoryFiles;
                }
            }
    
            public List<VirtualDirectory> _subDirs = new List<VirtualDirectory>();
            public List<VirtualDirectory> SubDirectories {
                get {
                    return _subDirs;
                }
            }
    
            public override IEnumerable Files {
                get {
                    return DirectoryFiles;
                }
            }
    
            public override IEnumerable Children {
                get { throw new NotImplementedException(); }
            }
    
            public override IEnumerable Directories {
                get { 
                    return SubDirectories;
                }
            }
        }
    

    And then write a unit test using that like so:

        [TestMethod]
        public void StyleBundleCustomVPPIncludeVersionSelectsTest() {
            //Setup the vpp to contain the files/directories
            TestVirtualPathProvider vpp = new TestVirtualPathProvider();
            var directory = new TestVirtualPathProvider.TestVirtualDirectory("/dir/");
            directory.DirectoryFiles.Add(new TestVirtualPathProvider.TestVirtualFile("/dir/style1.0.css", "correct"));
            directory.DirectoryFiles.Add(new TestVirtualPathProvider.TestVirtualFile("/dir/style.css", "wrong"));
            vpp.AddDirectory(directory);
    
            // Setup the bundle
            ScriptBundle bundle = new ScriptBundle("~/bundles/test");
            bundle.Items.VirtualPathProvider = vpp;
            bundle.Include("~/dir/style{version}.css");
    
            // Verify the bundle repsonse
            BundleContext context = SetupContext(bundle, vpp);
            BundleResponse response = bundle.GetBundleResponse(context);
            Assert.AreEqual(@"correct", response.Content);
        }