Dependency injection (DI) is very useful for isolated tests. In short:
Assumed you have a user table in the database. Assumed, that the user table is accessed through a class UserDatabase. Your application wants to cache users for quicker access. This means, that there is at least a list of users in memory. Usually you would wrap this list into a class UserManager. The typical code of UserManager will instantiate a UserDatabase, like:
class UserManager {
UserDatabase DB = new UserDatabase();
UserManager() {}
public UserId CreateUser() {
return DB.CreateUser();
}
...
}
If you want to test UserManager.CreateUser(), then the method needs a UserDatabase instance which will access the real database. This is not what you want, because it is not isolated. You want to test UserManager without activating UserDatabase. It rather would be cool if the UserManager would use a dummy database class which just simulates the behaviour of UserDatabase without actually writing to the real database.
So, we want to equip the UserManager for the test with a MockDatabase, but for the real operation it should use the real UserDatabase. We want to decide which database class the UserManager will be using under which circumstances. Means: we make the proper database and hand it to the UserManager. We inject it. Example:
class UserManager {
UserDatabase DB;
UserManager() { // real constructor
DB = new UserDatabase();
}
UserManager(UserDatabase db) { // test constructor
DB = db;
}
public UserId CreateUser() {
return DB.CreateUser();
}
...
}
The test code will create a new MockDatabase() and use UserManager(UserDatabase db) to inject it. The real code will use the default constructor, which makes its own normal UserDatabase. Not pretty, but possible. If there are more dependencies, then things get nasty with large contructors which are only used for tests and have different code from the real constructor. Not pretty.
There are better ways. For example DI frameworks, which provide sophisticated methods for injecting dependencies without bloating constructors. They use extensive configuration, if you like. I used StructureMap. It works, but I do not like, that I have to learn it. I spend a few hours reading, learning, trying, and even a few hours on it's unexpected limitations. Now I can use StructureMap, but I do not recommend it, if you do not already know it.
Dependency injection can be simpler. Here is my take: we want, that UserManager uses either MockDatabase or a UserDatabase. Why not just tell the UserManager to Use() either database:
class UserManager {
UserDatabase DB { get; set; }
UserManager Use(UserDatabase db) { DB = db; return this; }
public UserId CreateUser() {
return DB.CreateUser();
}
...
}
The test code will then look like:
var um = new UserManager().Use(new MockDatabase());
The production code will be very similar:
var um = new UserManager().Use(new UserDatabase());
The return this; part in Use() takes care, that the configuration can be written in a single line. If there are multiple dependencies, then I can do:
var um = new UserManager()
.Use(new MockDatabase())
.Use(new DummyWebService())
.Use(new TestController());
...because all the different Use() Methods have different parameters types and automatically know what to do. That's it. Use() is my DI framework.
And here comes my favorite single line of dependency injected configuration for a unit test. An ItemCore which uses a RezMockConnection and an ItemRepository which itself uses a MemoryItemStorage.Factory:
var rep = new ItemCore().Use(new ItemRepository().Use(MemoryItemStorage.Factory)).Use(new RezMockConnection()).Repository;
_happy_using()
Keine Kommentare:
Kommentar veröffentlichen