这里有一些很好的答案,但如果你正在寻找一个具体的原因,那就看看单元测试吧。
假设您想在业务逻辑中测试一个方法,该方法检索交易发生区域的当前税率。为此,业务逻辑类必须通过 Repository 与数据库对话:
interface IRepository<T> { T Get(string key); }
class TaxRateRepository : IRepository<TaxRate> {
protected internal TaxRateRepository() {}
public TaxRate Get(string key) {
// retrieve an TaxRate (obj) from database
return obj; }
}
在整个代码中,使用类型 IRepository 而不是 TaxRateRepository。
仓库有一个非公开的构造函数来鼓励用户(开发者)使用工厂来实例化仓库:
public static class RepositoryFactory {
public RepositoryFactory() {
TaxRateRepository = new TaxRateRepository(); }
public static IRepository TaxRateRepository { get; protected set; }
public static void SetTaxRateRepository(IRepository rep) {
TaxRateRepository = rep; }
}
工厂是唯一直接引用 TaxRateRepository 类的地方。
所以你需要一些支持这个例子的类:
class TaxRate {
public string Region { get; protected set; }
decimal Rate { get; protected set; }
}
static class Business {
static decimal GetRate(string region) {
var taxRate = RepositoryFactory.TaxRateRepository.Get(region);
return taxRate.Rate; }
}
还有另一个 IRepository 的其他实现 - 模型:
class MockTaxRateRepository : IRepository<TaxRate> {
public TaxRate ReturnValue { get; set; }
public bool GetWasCalled { get; protected set; }
public string KeyParamValue { get; protected set; }
public TaxRate Get(string key) {
GetWasCalled = true;
KeyParamValue = key;
return ReturnValue; }
}
因为实时代码(业务类)使用工厂来获取存储库,所以在单元测试中,您为 TaxRateRepository 插入 MockRepository。替换完成后,您可以硬编码返回值并使数据库变得不必要。
class MyUnitTestFixture {
var rep = new MockTaxRateRepository();
[FixtureSetup]
void ConfigureFixture() {
RepositoryFactory.SetTaxRateRepository(rep); }
[Test]
void Test() {
var region = "NY.NY.Manhattan";
var rate = 8.5m;
rep.ReturnValue = new TaxRate { Rate = rate };
var r = Business.GetRate(region);
Assert.IsNotNull(r);
Assert.IsTrue(rep.GetWasCalled);
Assert.AreEqual(region, rep.KeyParamValue);
Assert.AreEqual(r.Rate, rate); }
}
请记住,您只想测试业务逻辑方法,而不是存储库、数据库、连接字符串等……每个测试都有不同的测试。通过这样做,您可以完全隔离您正在测试的代码。
另一个好处是,您也可以在没有数据库连接的情况下运行单元测试,这使得它更快、更便携(想想远程位置的多开发团队)。
另一个好处是您可以在开发的实施阶段使用测试驱动开发 (TDD) 流程。我不严格使用 TDD,而是混合使用 TDD 和老式编码。