locked
Seeking Help With Using XUnit and Moq to Test an OWIN Web API Endpoint RRS feed

  • Question

  • User-94355476 posted

    I'm fairly new to writing Unit Tests and am challenged to find a solution to what I think should be something easy.  I've successfully built an ASP.NET Web API that uses the OWIN hosting approach.  My API is essentially a conduit between a Web App and the LibGit2Sharp library.  Everything works great but now I need to refactor it so that Unit Tests can be written.

    For demonstration purposes, here's the original code of one endpoint:

            [HttpGet]
            [Route("api/orgs/{orgName}/repos/{repoName}/files")]
            public HttpResponseMessage GetAllFiles(string orgName, string repoName)
            {
                List<File> files = new List<File>();
    
                try
                {
                    using (var repo = new Repository(SysIoHelper.GetRepoPath(orgName, repoName)))
                    {
                        var currentCommit = repo.Head.Tip;
                        foreach (TreeEntry treeEntry in currentCommit.Tree)
                        {
                            if (treeEntry.TargetType == TreeEntryTargetType.Blob)
                            {
                                File file = new File();
                                file.Filename = treeEntry.Path;
                                files.Add(file);
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    var error = new HttpError("Something went wrong trying to retrieve the list of files in the repo.");
                    return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, error);
                }
    
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, files);
                return response;
            }

    I then did a ton of reading on Dependency Injection (DI) and refactored it like this:

            [HttpGet]
            [Route("api/orgs/{orgName}/repos/{repoName}/files")]
            public HttpResponseMessage GetAllFiles(string orgName, string repoName)
            {
                _fileHandler.CreateNewFileList();
    
                try
                {
                    using (var repo = new Repository(SysIoHelper.GetRepoPath(orgName, repoName)))
                    {
                        var currentCommit = repo.Head.Tip;
                        foreach (TreeEntry treeEntry in currentCommit.Tree)
                        {
                            if (treeEntry.TargetType == TreeEntryTargetType.Blob)
                            {
                                _fileHandler.AddNewFile(treeEntry.Path);
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    var error = new HttpError("Something went wrong trying to retrieve the list of files in the repo.");
                    return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, error);
                }
    
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, _fileHandler.GetFileList());
                return response;
            }

    I tested this approach and it worked fine. 

    Obviously there's a whole lot more code to make all of this work but I feel that getting this method structured properly is 1/2 of the solution; the other half would be how to write the Unit Test code using XUnit & Moq.

    So I was all excited until I realized that this method still had the dependency of LibGit2Sharp, which constitutes much of the code in the middle of it.  While I'm confident that LibGit2Sharp will forever (or for a very long time) be what's called, I simply don't understand how to move forward from here.

    Here are some immediate questions:

    1. Is it correct to leave this code as is and simply have a dummy Git repo that gets created & destroyed with each set of unit tests?
    2. If, in fact, the LibGit2Sharp code also needs to get refactored then how can a unit test for that code ever be written using Moq?  In other words, that specific code which interacts with LibGit2Sharp is what really most needs to get tested.  If it's replaced with some generic code that can work with Moq then isn't the whole purpose of the Unit Tests diminished?

    Once again, forgive my naivety with this stuff.  I'm still learning and all of this refactoring of perfectly working code seems counter productive.  Yet it's demanded of me that Unit Tests be built.

    All insight is appreciated!

    Robert

    Wednesday, December 28, 2016 1:44 AM

All replies

  • User-94355476 posted

    Following up on my original post, two interesting things have come to light:

    1. I carefully studied the many tests employed in LibGit2Sharp: https://github.com/libgit2/libgit2sharp  It appears that most of them create a temporary Git repo, which then gets removed after all of the tests are complete.  This makes sense to me because they're actually testing the logic in (or in my case, "that deals with") the LibGit2Sharp library.  Put another way, they're not approximating what would be returned by a Moq representation of LibGit2Sharp.  I do accept that this might disqualify them from being pure "unit tests" but rather make them "integration tests".

    2. I followed this article by Mike Wasson, who is the author who clued me in to use the OWIN Web Api approach in the first place (rather than Web Hosting).  His approach is to simply reference the Web Api project from the test project and then perform calls back to it.  Using this approach, I just successfully wrote my first test:
            [Fact]
            public void GetAllFilesInRepo()
            {
                // Arrange
                ILogger logger = new FileLogger();
                IFileHandler fileHandler = new FileHandler();
    
                var controller = new Controllers.FilesController(logger, fileHandler);
                controller.Request = new HttpRequestMessage();
                controller.Configuration = new HttpConfiguration();
    
                // Act
                var response = controller.GetAllFiles("TestRepo", "MainRepo");
    
                // Assert
                var result = response.Content.ReadAsAsync<IEnumerable<File>>().Result;
                Assert.Equal(result.Count(), 5);
            }

    I carefully stepped through the code in both the test and in the endpoint method that is called.  It makes sense to me what's going on and the test passed.

    I'd love to hear from the experts out there, to see if the approach I've used is flawed or the conclusions I've drawn are incorrect.  The one caveat is that I'm looking for input from more of a pragmatic production perspective than a pure computer science one.

    Thank you!

    Wednesday, December 28, 2016 4:14 AM