views:

174

answers:

6

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 one 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.

+4  A: 

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.

Spoike
This will allow you to test the classes that use the IExcelReader, but not the actual ExcelReaderWrapper. So you would be able to verify that given some spreadsheet to save by the IExcelReader your other code works, but not that the ExcelReaderWrapper returns the spreadsheet data correctly. I think you can only test that with integration tests. Be wary of testing the mocks rather than the objects if you go this way.
Sam Holder
If you want the actual `ExcelReader` to be *unit tested* then the only way to do that is to rewrite it. I'm guessing it's a third-party thing and rewriting it would be unfeasable in terms of resources (i.e. manhours). The only way to be sure it is working is to write an integration test and cross your fingers that it works as promised by the third party.
Spoike
Sorry - no, I wrote the ExcelReader, it simply calls into the Office Interop and reads from the spreadsheet. The problem being that it does not work without the (slow) reading from the actual spreadsheet file, so I guess I'll go with moving it to integration tests.
Marcel Popescu
@Marcel: Then wrap the Office Interop and unit test the ExcelReader against a mock of the wrapper.
Spoike
Yes, this is what I'm going to do.
Marcel Popescu
+2  A: 

I'd go for the slow test. But you could make that an "integration test".

ammoQ
If you're calling Excel, it's essentially an integration test anyway.
RichardOD
Richard: excactly my thought
ammoQ
+1  A: 

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.

artemb
+1  A: 

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.

Sam Holder
Adding a bunch of "test" spreadsheets and moving it to another (integration tests) project sounds good. A lot of similar answers, it's going to be difficult to choose "the one" :)
Marcel Popescu
A: 

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.

jdehaan
+2  A: 

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.

Gishu
Wow. This is why I love SO :) Great answer, this is what I've been looking for.
Marcel Popescu
Yeap- I run slow tests overnight.
RichardOD