Снова запутался - строка подключения.
Снова запутался - строка подключения.
Дано:
Более-менее стандартный код получения данных из базы (тупо заполняем DataTable):
public DataTable FillDataTable(String sql)
{Debug.Print(sql);
OracleConnection oraCN = new OracleConnection();DataTable dt = new DataTable();
oraCN.ConnectionString = ConnectionString;
oraCN.Open();
OracleDataAdapter oraDA = new OracleDataAdapter(sql, oraCN);oraDA.Fill(dt);oraDA.Dispose();
oraCN.Close();oraCN.Dispose();return dt;}
Код - один и тот же для вин- и веб- приложений.
Есть примерно такая же имплементация для Ms SQL & PostgreeSql.
Есть наработки с использованием всех трех баз попеременно и одновременно.
Строка подключения получется так:
internal TMapNameToConnectionModel.Key Key { get; set; }
private string connectionString = null;internal string ConnectionString{get{if (connectionString == null){string temp = String.Empty;try{temp = TConnectionStringBuilder.GetConnectionString(Key);}catch (Exception ex){string name = TMapNameToConnectionModel.Instance[Key];TExceptionHandler.Error(ex, "Connection named '" + name + "' can't be obtained from config file");
}if (temp != null && temp != String.Empty)connectionString = temp;}return connectionString;}set{connectionString = value;}}
Т.е. если не известна - строим и кешируем.
Методика построения самой строки - не существенна - там несколько способов получения информации - несколько разных типов конфигов... Ну наследие, пока не починил...
Теперь - проблема.
Для тестирования в части тестов необходимо заменять строку подключеня - поинтить на тестовую базу.
Отсюда растут ноги у кеширования - поменял кеш - запоинтился на тестовую базу.
В данном
случае ошибок быть не должно - часть тестов - деструктивные - дропают таблицы.
Выполнить все тесты на тестовой базе - пока не могу - надо долго готовить данные, а ни времени, ни желания это делать нет.
Т.е. от кеширования строки отказаться пока не могу.
Второй момент - веб-приложения.
Веб-приложение работает с тремя разными (не тестовыми) базами.
Доступ к базам определяется содержимым web.config в папке текущей страницы.
Имена строк подключения - одинаковые - просто другое содержимое строки.
Но. после первого подключения строка уже закеширована и не загружается из конфига.
Т.е. кешировать строку мне нельзя.
Т.е. с одной стороны - не могу без кеширования, с другой - не могу с кешированием.
Ну или упрощенно - как кешировать некешируемое некешируя кешируемое?
Будут идеи как обойти проблему?
Делаешь проперти ConnectionString виртуальной и в тестах, где тебе надо заменить БД на тестовую, просто меняешь возвращаемое значение.
-----
Ну это Я делаю и без виртуальности.
Ааа... кажется понял что ты имеешь в виду - сделать несколько имплементаций калассов с FillDataTable & virtual ConnectionString и подставлять нужный в каждый отдельный момент.
Увы, но - не решение - просто проблема выбора нужной коннектион стринг заменена проблемой выбора нужного класса.
Сама проблема - осталась.
Поясню:
app.config
<connectionStrings><add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Nenagh)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED).../>
</connectionStrings>
в части тестов строка подключеня заменяется на
<connectionStrings><!-- DO NOT IN ANY CIRCUMSTANCES POINT Tests TO LIVE DATABASE - TESTS ARE DESTRUCTIVE -->
<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=SandBox)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../>
</connectionStrings>
Строка лежит в другом конфиге и читается отдельно.
Это - работает без проблем - в нужных тестах могу принудительно заменить одно на другое. И кешь мне в этом помогает - просто грубо меняю значение в кеше и все остальное работает без изменений.
На вебе немного по-другому.
web.config 1:
<connectionStrings><remove name="dbConnection" />
<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Nenagh)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../></connectionStrings>
web.config 2:
<connectionStrings>
<remove name="dbConnection" />
<add name="dbConnection" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=Lurgan)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED) .../>
</connectionStrings>
Здесь - строка заменяется - делается автоматически веб-сервером, но нет (не знаю, не нашел) нотификации. Если Я буду читать конфиг - получу нужное значение.
А читать Я не могу т.к. значение уже - требование подмены в тестах - кешировано - могу только переписать.
А проверять, как в тестах, где именно Я нахожусь - возможности нет - страница одна и та же, просто копия лежит в папке с другим конфигом и менять это не желательно. Да и дотятуться туда не просто - дизайнил чтобы было нельзя.
Т.е. Я не знаю нужно мне пользовать кеш или нужно читать конфиг. И возможности установить это вроде нет.
Хотя... принудительно перечитать конфиг и переписать коннектион в бизнес-объекте веб-страницы? Ну может быть... жал' что работа дублируется...
Ааа... кажется понял что ты имеешь в виду - сделать несколько имплементаций калассов с FillDataTable & virtual ConnectionString и подставлять нужный в каждый отдельный момент.
Не надо никаких нескольких имплетентаций. Одной имплементации достаточно.
public class SomeClass { public DataTable FillDataTable(String sql) { Debug.Print(sql); ... oraCN.ConnectionString = ConnectionString; ... return dt; } public virtual string ConnectionString { ... } }
Дальше есть 2 варианта:
1) делаешь свой фейк:
public class FakeSomeClass : SomeClass { public string TestConnectionString { get private set; } public FakeSomeClass (string testConnectionString) { TestConnectionString = testConnectionString; } public override string ConnectionString { get { return TestConnectionString; } } }
Далее в тесте используешь соответственно FakeSomeClass там, где нужно тестовая ДБ или оригинал, так где тестовая ДБ не нужна.
2) Используешь NSubstitute
В тесте просто пишешь:
[TestMethod] public void MyTest() { var someClass = Substitute.ForPartsOf<SomeClass>(); someClass.ConnectionString.Returns("My test connection string"); ... }
Ничего читать вообще не надо :)
Далее в тесте используешь соответственно FakeSomeClass там, где нужно тестовая ДБ или оригинал, так где тестовая ДБ не нужна.
-----
Тест (деструктивный, с дропом таблицы):
[TestClass]public class C_AT_Table_Tests{#region Tests Dataprivate Int16 UserID = 10;private String UrlAbsolutePath = @"http://myUrl/path/doc.dot?param";C_AT.Table table;#endregion#region Initalisation[ClassInitialize]public static void C_AT_Init(TestContext context){TEnvironmentValidator.CheckTestEnvironment("SetUp, CleanUp and tests can be run only on test environment");}[ClassCleanup]public static void C_AT_CleanUp(){//TEnvironmentValidator.CheckTestEnvironment("SetUp, CleanUp and tests can be run only on test environment");}[TestInitialize]public void Test_Init(){// drop existing dataSql.C_AT.Drop drop = new Sql.C_AT.Drop();Assert.IsNotNull(drop);TSysDba.SysDba.ExecCmd(drop);// create empty table for testSql.C_AT.Create create = new Sql.C_AT.Create();Assert.IsNotNull(create);TSysDba.SysDba.ExecCmd(create);// chech if there was an errorAssert.AreEqual(TExceptionHandler.Errors, 0);}[TestCleanup]public void Test_CleanUp(){Sql.C_AT.Drop drop = new Sql.C_AT.Drop();Assert.IsNotNull(drop);TSysDba.SysDba.ExecCmd(drop);}#endregion#region Tests#region Constructor Tests[TestMethod]public void C_AT_Table_Constructor_1(){table = new C_AT.Table();Assert.IsNotNull(table);//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table"));}[TestMethod]public void C_AT_Table_Constructor_2(){//table = (C_AT.Table)TLiOrderExFactory.Create(TKnownTables.C_AT);table = new C_AT.Table();string tn = table.GetType().ToString();Assert.IsNotNull(table);//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table,"));}#endregion#region Methods Tests[TestMethod]public void C_AT_Table_Insert_LogAccess(){//table = (C_AT.Table)TLiOrderExFactory.Create(TKnownTables.C_AT);table = new C_AT.Table();Assert.IsNotNull(table);table.Load(UserID);Assert.IsFalse(table.HasData);table.Insert_LogAccess(UserID, UrlAbsolutePath);table.Load(UserID);Assert.IsTrue(table.HasData);//Assert.IsInstanceOfType(table, Type.GetType("LiOrderEx.A.C_AT+Table,"));}#endregion#region Properties Tests#endregion#endregion#region Supportprivate void InsertTestRecord(){C_AT.Table table = new C_AT.Table();table.FillInitialData_1();foreach (C_AT.Row row in table){table.Save(row);}}#endregion}
Куда предлагается подставить FakeSomeClass?
Понимаю - будет вопрос по CheckTestEnvironment
public class TEnvironmentValidator{public static void CheckTestEnvironment(){CheckTestEnvironment("Thoose tests can be run ONLY on the test environment.");}public static void CheckTestEnvironment(string pMessage){DbExec.Interfaces.ITestConnection connection = TExec.Instance as DbExec.Interfaces.ITestConnection;Assert.IsNotNull(connection);if (!connection.IsTestEnvironment)Assert.Fail(pMessage);}}
Отсюда нельзя поменять ничего, Специально делал с невозможностью - защищался от возможных ошибок. Только проверка можно ли их раннать.
Если копать глубже, то упрусь в что-то закешенное - инстансе SomeClass или потомка - та же ситуация что и со строкой, но на другом уровне.
Подумал... хммм... в принципе именно твое предложение и реализовано. Только не наследованием, а имплементацией определенного интерфейса.
Но проблема - смена строки подключения - не ресолвится...
Видимо надо модифицировать TMapNameToConnectionModel.Key - добавлять признак - win-, web-, test-...
и модифицировать построитель строки...
Мда, ну и тестики у тебя :) Жуть просто.
Ладно, я так понимаю, что проблема твоя в том, что при инициализации и при отчистке у тебя вызывается TSysDba.SysDba.ExecCmd(...), где ExecCmd - это FillDataTable из первого поста, SysDba - соответственно SomeClass из моего ответа.
Я так понимаю, что TSysDba.SysDba - это реализация синглтона. Соответственно тебе надо заинжектить фейк в этот синглтон.
Видимо надо модифицировать TMapNameToConnectionModel.Key - добавлять признак - win-, web-, test-...
и модифицировать построитель строки...
Для начала было бы неплохо прочитать книжку по тестированию :) Рекомендую вот эту: The Art of Unit Testing (в сети есть бесплатно в pdf)
Хммм... кажется нащупал на чем прошлый раз прервали...
Делается так - пишется стандартный конфиг в нужном варианте - для простых тестов - подключение к живым базам, для деструктивных - к тестовым, для веба - как и было - по конфигу в нужной папке.
Кеширование в текущей имплементации - надо удалять и читать инфу из текущего конфига. Это решит проблему с доступом к актуальной строке подключения. Сложные обертки будут лишними, останется только кроссировка - модель(завод+база) => имя подключения. Должно быть dостаточно гибко для управления выбором строки.
Проблема с деструктивными тестами решается помещением оных в отдельную дллку (можно - много дллок, но не смешивать с тестами требующими живой базы).
Теперь надо придумать как жить с двумя app.config в конечной папке тестов.
Мелкомягкие не шибко любят раскиданные по папкам дллки.
Может заменить имя конфига?
Надо смотреть как это будет в тестах.
А может быть ты просто примешь, что БД уже протестирована и не нуждается в дополнительном тестировании? Кроме того, даже если в БД и есть какая-то ошибка, ты все равно не сможешь ее исправить.
В результате ты придешь к нормальным юнит-тестам, которые будут тестировать исключительно твой код и не будут зависеть от сторонних библиотек.
Жуть просто.
-----
Хорошо, что хоть какие-то есть - до меня вообще ничего не было.
Вот покрытие мне не нравится. Мало - тесты пишутся только на то, где возникают проблемы.
Соответственно тебе надо заинжектить фейк в этот синглтон.
-----
Неа... не надо инжектить фака никуда. Не будет фака. С факом Я все одно запутаюсь и помру на поиске ошибки.
Ну не Я - так пришедший мне на смену - пока разберется - угробит базы несколько раз.
Нужно "топорное" решение.
Топорным будет - подмена не строки - пусть она читается как читается стандартным методом, но замена имени строки которую надо читать - ее возьму из мапа.
Как страховку от дурака - оставлю проверку куда запоинтено - у серверов статические ИП - в деструктивных тестах.
Плохо - часть хардкожена, но все в маленькой кучке и прозрачно - один раз при смене ипишников можно поменять.
так будет поменьше писанины по изменению текущего кода.
Сейчас перепроверю как ужится с двумя app.config и буду писать...
Хотя... можно и без второго... вроде... не помню... устал...
Рекомендую
-----
Есть и даже читана.
Просто уже не надо - осталось 13 месяцев работы - не успею поправить даже 10% наличного кода.
Так Я не базу тестирую.
Я из базы беру данные для тестирования.
Можно - мокать, но написание моков на базу в 700+ таблиц и весом в 22 Гб... хммм...
Просто нет времени выбрать нужные данные - пользую живые базы. Для недеструктивных тестов.
Есть ситуации, которые долго ждать в живых базах, либо которые не живут долго в живых базах.
Для них делаются тестовые данные в тестовых базах. Эти тесты - деструктивные. Не мокаю чтобы не выбиваться из структуры.
И да - скорость тестов меня не беспкоит - этим CI занимается - через час-другой переварит - нормально.
ты все равно не сможешь ее исправить.
-----
От меня этого и не ждут.
От меня ждут определения наличия и характера ошибки.
Как в базе, так и в нашем самопальном софте.
Ну и устранения где можно или рекомендаций по тому как не попадать в ситуации там где нельзя.
Лечение ЦВСки с аппострофами в именах вида - O'Brain, O'Conner - лечу заменой аппострофов в своем коде.
Тоже самое, но с введенным оператором три недели назад оппострофом - запрещаю его пользовать - не могу починить не свой код.
В результате ты придешь к нормальным юнит-тестам
------
Не приду. Времени нету.
Точнее - приду, но не на этом проекте.
Пишутъ что веба тоже мона
https://www.c-sharpcorner.com/article/how-to-changeswitch-...
https://stackoverflow.com/questions/40836102/asp-net-core-...
https://entityframework.net/knowledge-base/43993918/dynamic-connection-string-for-entity-framework-code-first-for-multiple-database-providers-at-runtime-ef6---mssql--mysql--oracle--
Сменить - не проблема.
Проблема - знать когда сменить.
Ну да ладно - все нужные строки идут в конфиг и читаются из него стандартным методом.
Разбирательство и управление идут через смену имени выбираемой строки. Вот там пока немного заморчно сделано. Надо смотреть.
Через имя мне показалось сделать проще - не надо разбираться с тем куда коннектится - МС СКЛ, Оракле, Постргрее...
Починил.
Починил именно способ выборки строки подключения.
Дополнительно - очистился от груды "ненужного" кода в методах выборки строки подключения.
Полегчало это безобразие существенно.
Кроме этого - перебрал одну из ДАЛ-библиотек и ее тесты. Полтора дня.
Еще не прогнал тесты - надо убедится, что ни при каких обстоятельствах эти тесты не полезут
в рабочую базу.
Осталось - перебрать еще 96 почти таких же либ... и проверить что и они не полезут в живую базу.
Потом еще под 100 бизнес-объектов с веб-страниц подогнать под "Новый Стандарт" кода... там
как раз наоборот - должны работать с живыми данными.
Итого: 150 дней + 100 дней = 250 рабочих дней... т.е. полный рабочий год.
Жаль, что потом все это пойдет в мусорник - с переходом на новую систему нихрена из этого
работать не будет - там набор таблиц и структура таблиц - другие...