How to unit test reading an Excel reader?
I have an ExcelReader
class in my C# application - I need to import Excel spreadsheets into database tables. My problem is that this is one of the few untested classes - I cannot use a "mock input" for it, and testing with an actual Excel spreadsheet makes the test a bit slow (about one second). I know the class is working correctly - I tested it manually - but I am a bit uneasy about leaving it without its own tests.
So - my question is: which o开发者_JAVA技巧ne is better: no unit tests, slow unit tests, or a third way I cannot figure out?
[Edit] A lot of great answers, I'm really sorry I can only mark one.
You can wrap the ExcelReader
and mock the wrapper. An easy example would be:
+--------------+
| {interface} |
| IExcelReader |
+--------------+
^
|
|
+-------------+ 1 1 +--------------------+
| ExcelReader |-----------| ExcelReaderWrapper |
| | | |
+-------------+ +--------------------+
That way any class that needs the actual ExcelReader
can reference to an IExcelReader
instead, and be injected by an ExcelReaderWrapper
or a mock of it. It may create a lot of classes but the payoff lies in that the ExcelReader can be replaced and you won't be forced to write a slow integration test.
How about this?
ExcelReader --- has --- ExcelInterop (interface) --- to read --- .xls
I'm assuming that ExcelReader has more logic than calling Excel Interop Library functions. If that is the case, write fast unit tests for ExcelReader by slotting in a mock implementation of the ExcelInterop. You can use this to mock/stub calls to the interop library.
Next write contract tests for the ExcelInterop interface. The test subject here would be a wrapper class (the real interface implementation) that delegates to the actual MS Excel interop assembly. These tests should verify that the APIs that you use work as you expect it to ; run this against a golden/reference .xls
Mark the second group of tests with a 'LongRunning' or equiv attribute and run them less frequently (on the build machine/once at EOD) if they are too slow.
I'd go for the slow test. But you could make that an "integration test".
Usually unit tests do not involve file system or database or any shared resource. A test that would read a file from the FS and load it into a DB is commonly called an integration test.
I usually separate integration tests into a separate group which is OK to run slow (a couple of minutes total).
You can also refactor your ExcelReader and extract the low-level part which requires a real file to read into a separate class. Then you can mock it and test other logic in a unit test.
I'd create integration tests for it, with a series of defined spreadsheets which are used by the tests to test certain functionality, testing that the expected output is produced. I'd commit the spreadsheets to the source control. I'd also have the tests in a different project to the unit tests.
We have also a big bunch of unit tests regarding excel, from experience it is good to have them run regularly witout mocking. Especially the COM communication is a bit tedious and there are limitations (depending on the way you read) in the size of strings writable/readable from Excel. I remember something about a 911 char limitation I discoverred by testing. For some very long tests (>15 min) we add a special category attribute "LongDuration" to have them run only in the nightly builds.
精彩评论