Здесь есть несколько отличных ответов, но если вы ищете конкретную причину, не смотрите дальше, чем модульное тестирование.
Предположим, что вы хотите проверить метод в бизнес-логике, который извлекает текущую ставку налога для региона, где происходит транзакция. Для этого класс бизнес-логики должен общаться с базой данных через репозиторий:
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; }
}
Поскольку оперативный код (бизнес-класс) использует Factory для получения репозитория, в модульном тесте вы подключаете MockRepository для TaxRateRepository. После подстановки вы можете жестко закодировать возвращаемое значение и сделать базу данных ненужной.
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 и кодирования старой школы.