且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用EPPlus的单元测试类

更新时间:2023-11-16 22:23:10

模拟第三方库常常使人痛苦不堪,并创建了难以理解的单元测试。



每个测试都应该简短,易读且易于理解。



通常***围绕第三方库创建包装器类,并在其上使用接口,这样才能更好地理解预期的成功操作。



这些课程。然后,您可以创建实现这些接口的模拟对象,仅用于测试。



不过,说起来容易做起来容易。显然,第三方库会发生某些事情,这些事情不仅会被切断代码并进行有意义的测试。



在这种情况下,您仍然应该使用您自己的界面,但是将那些类型的单元测试隔离到仅依赖于第三方库的最低限度。



尝试看看SOLID编程模式。使用这种模式构建的系统通常更易于测试,因为所有组件都是松散耦合的。



http://zh.wikipedia.org/wiki/Solid_(面向对象的设计)


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)