I am writing automated functional tests for my application. To interact with the application, I use the SQL database to retrieve data and validate data.
One of the challenges that I'm facing is that some data that I need to pull at run time can take 5 minutes to execute a SQL query (specifically this query GetAccessionNumber()), significantly impacting the performance of my tests, especially when most of the tests need to do this query. Also, I foresee that the query in GetAccessionNumber might change at run time.
Although I am currently talking to the DB to retrieve my data, I wonder if there is a good design pattern or strategy for such a situation to improve my test code. Also, I want to do this efficiently so that I don't waste 5 minutes every test just reading data. Maybe executing all the queries at Setup() and use all the data for my tests and then TearDown() after?
Someone suggested populating the data that I need into like a "test db" and then using that through my tests, rather than reading the entire db (which also contains irrelevant data for my purposes). How would you do that? Let's say I have about 30 queries that I need to run every single build to get the latest test data. Should I run that before all of my tests through Setup() method? Or should I run these queries externally, populate my own testing table, and then let the tests use this new "test db"?
I am adding a working sample of the code to better convey my problems.
[Test]
[Retry(2)]
[Description("Validate that the pop up can open and close when it is clicked.")]
public void ToggleOpenAndClose()
{
var home = new HomePage(Driver);
home.GoTo();
home.Login();
var adminReset = new AdminResetPage(Driver);
adminReset.GoTo();
//todo not sure if this is the right implementation
adminReset.SetLoginId(TestData.TestData.LoginId);
adminReset.SetBookletTo(TestData.TestData.AccessionNumber);
var assPage = adminReset.GoToBookletLocation();
assPage.ShortTextPopup.ClickPopUp();
assPage.ShortTextPopup.ClosePopUp();
Assert.IsFalse(assPage.ShortTextPopup.PopupIsOpen(), "The pop up did not close after trying to close the pop up");
}
public static class TestData
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
static readonly string _connectionString = string.Format(
"Data Source=.\\foobar_2017;" +
"Initial Catalog={0};" +
"User ID={1};" +
"Password={2};",
ConfigurationManager.AppSettings["DB.DataBaseName"],
ConfigurationManager.AppSettings["DB.UserId"],
ConfigurationManager.AppSettings["DB.Password"]);
//todo temporarily hardcoded until we implement the sql reader
public static string LoginId
{
get { return "P2abc"; }
}
public static string AccessionNumber
{
get { return "AC123"; }
//todo temp hardcoded for debugging
//get { return GetAccessionNumber(_connectionString); }
}
public static User GetUser(UserType userType)
{
if (Config.IsEnvironmentLocalHost())
return GetLocalhostUser(userType);
if (Config.IsEnvironmentDev())
return GetDevUser(userType);
return GetTestUser(userType);
}
private static User GetTestUser(UserType userType)
{
switch (userType)
{
case UserType.Admin:
return new User() { Password = "myPass", UserName = "myUserName" };
default:
throw new ArgumentOutOfRangeException(nameof(userType), userType, null);
}
}
private static User GetDevUser(UserType userType)
{
switch (userType)
{
case UserType.Admin:
return new User() { Password = "myPass2", UserName = "myUserName2" };
default:
throw new ArgumentOutOfRangeException(nameof(userType), userType, null);
}
}
private static User GetLocalhostUser(UserType userType)
{
switch (userType)
{
case UserType.Admin:
return new User() {Password = "admin$", UserName = "password$"};
default:
throw new ArgumentOutOfRangeException(nameof(userType), userType, null);
}
}
private static void GetStudentLoginId(String connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
//ReadSingleRow((IDataRecord)reader);
}
reader.Close();
}
}
private static string GetAccessionNumber(string connectionString)
{
List<string> accessionNumbers = new List<string>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
var query = "select AccessionNumber from Item where " +
"ContentHtml like '%short-text-popup%' " +
"and AccessionNumber not like '%help%' ";
//"ContentHtml like '%short-text-popup%'" +
//"and AccessionNumber not like '%help%' ";
SqlCommand command = new SqlCommand(query, connection);
command.CommandTimeout = 300;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
accessionNumbers.Add(reader["AccessionNumber"].ToString());
}
reader.Close();
}
return accessionNumbers[0];
}
}
Basically, I am wondering if there is some kind of a design pattern that I should be using to make sure that I properly talk to the database for the test data.
Can you clarify whether you're currently talking properly to the database with your test data? If so, I'll retract my close vote. If not, you're asking for something that isn't implemented yet. That's what we mean with 'code not yet written'. – Mast yesterdayTestData
is taking 5 minutes, as you say in the question. Is there something else that you haven't shown us? – 200_success♦ yesterdayGetStudentLoginId
for example just reads a bunch of rows an ignores the result.. the main query that is the issueGetAccessionNumber
isn't actually called etc. – forsvarir yesterday