Search code examples
c#asp.netjsonhttpcontextnancy

xUnit Making tests for reading and writing to Json file


I'm creating a new application in NancyFx and I'm trying to do Test Driven Development (TDD) So the first features I want to develop are reading and writing to a json file. Now I know this is quite hard to do because it involves httpContext which I obviously can't do unless the application is running.

What I would like to do is mock this so I can create a unit test for reading and writing to a json file. Could anyone show me an example of how I might do this and explain how you have done it?

I have two methods one for reading and one for writing shown below:

ReadToJsonFile Method:

public IEnumerable<T> ReadFile<T>(string fileName)
    {
        try
        {
            var readFile = File.ReadAllText(HttpContext.Current.Server.MapPath(fileName));
            var list = JsonConvert.DeserializeObject<List<T>>(readFile);
            return list;
        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
            return null;
        }
    }

WriteToJsonFile Method:

public bool WriteFile<T>(string fileName, IEnumerable<T> list)
    {
        try
        {
            var listContent = JsonConvert.SerializeObject(list, Formatting.Indented);
            File.WriteAllText(HttpContext.Current.Server.MapPath(fileName), listContent);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
            return false;
        }
    }

Any suggestions would be brilliant, Thanks.


Solution

  • I suggest that you employ an adapter or facade to abstract away from using System.IO static methods:

    public class JsonWriter
    {
        private readonly IFileSystem _file;
        private readonly HttpContextBase _httpContext;
        private readonly ILog _logger;
    
        public JsonWriter(
            IFileSystem file, 
            HttpContextBase httpContext,
            ILog logger)
        {
            _file = file;
            _httpContext = httpContext;
            _logger = logger;
        }
    
        public bool WriteFile<T>(string fileName, IEnumerable<T> list)
        {
            try
            {
                var listContent = JsonConvert.SerializeObject(list, Formatting.Indented);
                _file.WriteAllText(_httpContext.Server.MapPath(fileName), listContent);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogException(ex);
                return false;
            }
        }
    
    }
    

    You'll need an interface like this:

    ///<summary>Implementors are wrappers for static methods of System.IO.File and System.IO.Directory
    ///</summary>
    public interface IFileSystem
    {
    
        ///<summary>Creates a new file, writes the specified string to the file, then closes the file. If the file already exists, it is overwritten.
        ///</summary>
        ///<param name="path">The file to write to</param>
        ///<param name="contents">The string to write to the file</param>
        void WriteAllText(string path, string contents);
    
        ///<summary>Creates a new file, writes the specified string to the file, then closes the file. If the file already exists, it is overwritten.
        ///</summary>
        ///<param name="path">The file to write to</param>
        ///<param name="contents">The string to write to the file</param>
        ///<param name="encoding">An <see cref="Encoding"/> object that represents the encoding to apply to the string</param>
        void WriteAllText(string path, string contents, Encoding encoding);
    
    }
    

    And an implementation like this:

    ///<summary>Replaces the static methods of System.IO.File
    ///</summary>
    public class FileSystem : IFileSystem
    {
        ///<summary>Creates a new file, writes the specified string to the file, then closes the file. If the file already exists, it is overwritten.
        ///</summary>
        ///<param name="path">The file to write to</param>
        ///<param name="contents">The string to write to the file</param>
        public void WriteAllText(string path, string contents)
        {
            File.WriteAllText(path, contents);
        }
    
        ///<summary>Creates a new file, writes the specified string to the file, then closes the file. If the file already exists, it is overwritten.
        ///</summary>
        ///<param name="path">The file to write to</param>
        ///<param name="contents">The string to write to the file</param>
        ///<param name="encoding">An <see cref="Encoding"/> object that represents the encoding to apply to the string</param>
        public void WriteAllText(string path, string contents, Encoding encoding)
        {
            File.WriteAllText(path, contents, encoding);
        }
    }
    

    Now you can mock the file methods and the HttpContext in your unit tests.