views:

388

answers:

3

Hello all,

I've recently got a task to create a simple utility that allows to import data from a file with special format to the database. I've implemented console application with few classes(Program class operates with business logic class, business logic class in turn operates with data access class). Everything works ok, but now I'm thinking about creating some unit tests and refactoring application (I have not created real unit tests before, just a bunch of integration tests a long time ago, so I believe this application is perfect field for practicing).

So, here is the problem: the data access class has been made static, this doesn't allow to mock it and as a result create real unit tests. To fix this I need to create an interface and implement it in the data access class. Also I will have to add a constructor to the business logic class that will accept parameter of that interface type. So this means that I will end up creating data access class in the application Main() method and something tells me this not the best approach (is it really ok that the entry point should know about some data access things? what if the chain is much longer or there should be several chains?). I know I can use some IoC container, but I think this is too simple application to use containers.

Thanks!

A: 

Here is a simple solution: Instead of calling you data access class directly, use helper methods:

  public void insert (...) {
      DataAccess.insert (...);
  }

Now you can override these calls. I suggest to split the tests like so:

  1. Create a couple of tests which make sure that DataAccess does the right thing when it gets the right parameters.

  2. In the mockup tests, simply collect the parameters sent to insert(). Don't call DataAccess at all.

The tests in #1 will make sure that writing the data into the DB will work while the tests in #2 will make sure that you call DataAccess with the correct values. The latter tests will run very quickly, which will make it easy to test special cases, etc.

You don't need to run the tests from #1 all the time, either. Only when you change something in DataAccess or before a release. This will make testing efficient and pleasant.

Aaron Digulla
added comment as answer, hadn't much space
Dev
@Dev - responding with an answer can get confusing very quickly. Eventually the answers will be re-ordered, Aaron may change his name, or he may even delete his answer. I'd recommend answering in comments - if you need more room than the allotted characters, you can spread the response across two comments. Alternately, you could edit your original question to contain some of the information if you think it will help folks answer you.
Jeff Sternal
Thanks Jeff, will move that "answer" to comments.
Dev
@Aaron,Thanks for the quick reply. If I understand you correctly, the proposition is to wrap data access class with some wrapper with virtual methods, so I can than override them for mocking. Interesting idea, but it seems that I will need to "duplicate" all data access methods for the purpose of testability only and every time I need to add new method to the data access class I will have to create an appropriate wrapper. Is that ok if I create one virtual method in the BL class that will create data access object?
Dev
Is that ok if I create one virtual method in the BL class that will create data access object? Something like this:protected virtual IDataAccess CreateDataAccess() { return new DataAccess(); }Then I will be able to override single method to mock data access object. What are disadvantages of such approach?
Dev
In my case, I just had to override the methode `executeUpdate(sql, param...)`, so that was enough. If you have lots of methods, use an interface.
Aaron Digulla
+1  A: 

Assuming that you are using LINQ to SQL, maybe you could use the repository pattern to wrap the DataContext into an interface that you can later mock, thus making unit testing possible.

There are some articles about this subject around the internet, here is one: http://andrewtokeley.net/archive/2008/07/06/mocking-linq-to-sql-datacontext.aspx

Konamiman
Right now I'm using typed dataset with custom methods to read from file and write to file. I didn't use entity framework or linq2sql initially, because at the time I had to write this application I hadn't much time for experiments with the technologies I'm not experienced in. But this is good idea, maybe I will rework my DAL.
Dev
+4  A: 

I need to create an interface and implement it in the data access class. Also I will have to add a constructor to the business logic class that will accept parameter of that interface type. So this means that I will end up creating data access class in the application Main() method and something tells me this not the best approach (is it really ok that the entry point should know about some data access things? what if the chain is much longer or there should be several chains?)

On the contrary! This is the best approach, at least from a testability perspective.

The only way to make your business logic layer testable is to isolate it from your data access layer by doing exactly what you're contemplating.

Your top-level application is where the buck stops - it's the only component that should need to know what the concrete data access class is.

If the chain is much longer or there are several chains, that's no big deal (though you may want to consider collapsing some application layers if it gets out of hand). Consider this potential code in a Model-View-Presenter app's View, where the Presenter has a dependency on a CustomerService, which has a dependency on a Repository and a dependency on an AccountingService (which is also dependent on the Repository):

public CustomerView() {
    IRespository       repository        = new ConcreteRepository();
    IAccountingService accountingService = new ConcreteAccountingService(repository);
    ICustomerService   customerService   = new ConcreteCustomerService(accountingService, repository)
    this._Presenter = new CustomerPresenter(customerService);
}

Finally, there's no need to use a dependency injection container if you don't want to (though some of them are surprisingly lightweight) - dependency injection by hand works fine until you start repeating yourself all over the place (or find you want to configure the dependencies at runtime).

Jeff Sternal
+1: Nice answer! I especially like the "where the buck stops" line. Good comments on DI Containers, too!
TrueWill
@Jeff - thank you, I will give it a try. Though my pessimistic being tells me there should be some hidden pitfalls :)
Dev
@Dev: Skepticism is next to godliness! ;)
Jeff Sternal
Ok, I've added interface for data access and passing it from the application top level. Works fine. But how to be with file system access? For example, I have a function that loads data from a database into dataset, formats loaded data (formats are stored in some external xml file) and finally serializes dataset to a file.
Dev
So, again, to support testability I need to move all the functions that access file system to some interface. But first - I find it quite handy to just call dataset.WriteXml(file), but for the testability I have to create interface and move dataset.WriteXml() to its implementation, which looks to me as unnecessary layer and makes code less obvious. And second - if I move all methods that access file system to a single interface, it will violate SRP principle, because serializing\deserializing datasets and reading data formats from a file seems to be different responsibilities, correct?
Dev
There's a lot to chew on there, and I think it's worth posting a follow-up question (I tried posting an answer in comments, but it took three just to get my point across poorly, so I deleted them). Having said that, if you don't feel like doing that, there are some obnoxious cases where it's really difficult to write testable code without introducing otherwise superfluous interfaces (I've had this problem with the SqlConnection's InfoMessage event recently). On the other hand, sometimes these only seem superfluous -> there's more to say about the single-responsibility principal here!
Jeff Sternal
@Jeff - Thanks, I've posted another question - http://stackoverflow.com/questions/1461016/file-access-unit-testing-dependency-injectionWill be glad to see your comments )
Dev