简体   繁体   中英

Unit testing classes that use EPPlus

I am having issues unit testing classes that use EPPlus. In my mind, I have two options.

I can mock & inject the HttpPostedFileBase into a method, or I can mock & inject the EPPlus ExcelPackage class.

Mocking the HttpPostedFileBase, at least doing a true mock, seems limited. I can mock the basic properties on the file (MIME type, filename, etc), but to mock its InputStream in a way that allows the tests to actually interact with it, seems extremely difficult. The only solution I can come up with is to provide a real excel file, create a real FileStream with it, and assign that FileStream to my mock HttpPostedFileBase's InputStream. But then it's technically an integration test, not a unit test.

const string FakeFileName = "TestExcelFile.xlsx"; // path to an actual excel file
var fileStream = new FileStream(FilePath, FileMode.Open);
var fakeFile = A.Fake<HttpPostedFileBase>();
A.CallTo(() => fakeFile.InputStream).Returns(fileStream);



I figured if I wanted to do an actual unit test, I could mock and inject the EPPlus ExcelPackage class instead. I could then mock the related Worksheet, Columns & Cell classes, setting their properties dynamically to fit the conditions of my test, while never touching a real file. The problem is, most of the EPPlus classes are sealed, so I can't mock them with FakeItEasy. I tried creating wrapper classes for them (see below), so I could mock the wrapper class instead... but some of the classes I need to mock/wrap have internal constructors, so I can't instantiate them. (I did try getting around the internal constructor problem using a couple of ugly hacks, but didn't have success.) And so I've hit a wall with this option.

I am still a novice and have a lot to learn. Perhaps my concept of a wrapper class is incorrect, and I am doing it wrong. Is there a way around this I can't see, or should I just give up, use a real excel file, and call it an integration test? So far, that's what I am leaning towards.

public class ExcelWorksheetsWrapper : IEnumerable<ExcelWorksheet>
{
    public readonly ExcelWorksheets _excelWorksheets;

    public ExcelWorksheetsWrapper()
    {
        // internal constructor, can't instantiate
        _excelWorksheets = new ExcelWorksheets();       
    }

    public ExcelWorksheet Add(string worksheetName)
    {
        return _excelWorksheets.Add(worksheetName);
    }

    public IEnumerator<ExcelWorksheet> GetEnumerator()
    {
        return _excelWorksheets.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _excelWorksheets.GetEnumerator();
    }
}

Mocking a third-party library is often a pain in the neck and creates cryptic unit tests.

Each test should be short, easy to read and understandable. It should be easy looking at the test to understand what the intended successful operation should be.

It's usually better to create wrapper classes around third-party libraries, and use interfaces on those classes. You can then create mock objects that implement those interfaces just for testing.

Still, that is easier said then done. Obviously there are going to be things that third-party libraries do that can't just be cut-out of code and make for meaningful tests.

In those cases, you still should use your own interfaces, but isolate those kinds of unit tests to just the bare minimum that are dependent upon the third-party library.

Try taking a look at the SOLID programming pattern. Systems built using that pattern are often easier to test because everything is loosely coupled.

http://en.wikipedia.org/wiki/Solid_(object-oriented_design)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM