I've made an attempt to unit test a methods in the database layer of my app. In my test I was planning to write mocks to avoid having a live connection to MySQL.
I'll write down a simplified version of my code to illustrate. Let's start with the unit test:
[TestMethod()]
public void TestMethod1()
{
DataBaseInterface database = new DataBaseInterface(); // this is the class which has the method I want to unit test
DataSet ds = new DataSet();
ds.Tables.Add(new DataTable("signals"));
ds.Tables[0].Columns.Add(new DataColumn("ID"));
ds.Tables[0].Columns.Add(new DataColumn("Content"));
DataRow row = ds.Tables[0].NewRow();
row["ID"] = "1";
row["Content"] = "Foo";
ds.Tables[0].Rows.Add(row);
MockDataBaseConnection db = new MockDataBaseConnection();
string result = database.GetContent(1); // this is the method I want to unit test
}
The method I want to unit test looks something like this:
public class DatabaseInterface
{
private MySqlConnectionAdapter _sqlConn;
public string GetContent(int i)
{
MySqlCommand command = _sqlConn.CreateCommand();
command.CommandText = String.Format("");
_sqlConn.Open();
MySqlDataReader reader = command.ExecuteReader();
/// more code here
}
To prevent using an actual database connection in my unitest I wrote a few mockups and here lies the problem.
The mockups looks like this:
public class MySqlConnectionAdapter
{
public MySqlConnection _sqlConn;
public MySqlConnectionAdapter()
{
}
public MySqlConnectionAdapter(string con)
{
_sqlConn = new MySqlConnection(con);
}
public event MySqlInfoMessageEventHandler InfoMessage
{
add
{
_sqlConn.InfoMessage += value;
}
remove
{
_sqlConn.InfoMessage -= value;
}
}
public virtual event StateChangeEventHandler StateChange
{
add
{
_sqlConn.StateChange += value;
}
remove
{
_sqlConn.StateChange -= value;
}
}
public virtual void Open()
{
_sqlConn.Open();
}
public void Close()
{
_sqlConn.Close();
}
public string ServerVersion
{
get
{
return _sqlConn.ServerVersion;
}
}
protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
{
return _sqlConn.BeginTransaction(isolationLevel);
}
public void ChangeDatabase(string databaseName)
{
_sqlConn.ChangeDatabase(databaseName);
}
public string ConnectionString
{
get
{
return _sqlConn.ConnectionString;
}
set
{
_sqlConn.ConnectionString = value;
}
}
public virtual MySqlCommand CreateCommand()
{
return _sqlConn.CreateCommand();
}
public string DataSource
{
get
{
return _sqlConn.DataSource;
}
}
public string Database
{
get
{
return _sqlConn.Database;
}
}
public ConnectionState State
{
get
{
return _sqlConn.State;
}
}
}
public class MockDataBaseConnection : MySqlConnectionAdapter
{
public DataSet ds;
public new MockMySqlCommand CreateCommand()
{
return new MockMySqlCommand(ds);
}
}
public class MockMySqlCommand
{
private DataSet _ds;
public MockMySqlReader ExcecuteReader()
{
return new MockMySqlReader(_ds);
}
public MockMySqlCommand(DataSet ds)
{
_ds = ds;
}
}
public class MockMySqlReader
{
public DataSet _ds;
private int currRow = 0;
public MockMySqlReader(DataSet ds)
{
_ds = ds;
}
public bool Read()
{
if (currRow < _ds.Tables[0].Rows.Count)
{
currRow++;
return true;
}
else return false;
}
public object GetValue(int i)
{
return _ds.Tables[0].Rows[currRow][i];
}
}
When the line _sqlConn.CreateCommand() is executed it throws and exeption. Why is that?
The _sqlConn fiels in my unit test method is a MockDataBaseConnection object and I want it to return a MockMySqlCommand object.
The sealed classed of the MySQL dll is giving me a headache. Can anyone explain how I can fix my code or show a simple example of how to unit test a method
which queries a database without actually querying the database.
Like suggested by vulkanino, I think it would be a lot easier to setup a local database instance, and run the tests directly on it.
I've been in the same situation, and developed this project that simply sets up a local running MySql test server instance, without you having to care about server administration: https://github.com/stumpdk/MySql.Server
Related
I would like send connection for report in DevExpress, and send query for this report I call report with code bellow:
private void Form14_Load(object sender, EventArgs e)
{
try {
XtraReport report = XtraReport.FromFile(#"C:\\a\\Report2.repx", true);
ReportPrintTool printTool = new ReportPrintTool(report);
printTool.ShowPreviewDialog();
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
In the general case, your report is connected to data via the SqlDataSource component. So, I suggest you update the connection at the SqlDataSource instance level. For that purpose, just get the existing data source from your XtraReport class:
var sqlDataSource = xtraReport.DataSource as SqlDataSource;
sqlDataSource.ConnectionParameters = ...;
Please refer to the Specify data connection article to learn how to change the data source's connection parameters.
You can create your own report implementation where you can update the connection string by checking the connection strings used in the reports and the sub reports.
In below example, a Typed Dataset is used to create the reports and then you can update the connection string of the table adapters on the initialization.
Refere this below implementation:
// Base Report
public partial class BaseReport: DevExpress.XtraReports.UI.XtraReport, IDisposable
{
public List<SQLiteConnection> GetConnectionList
{
get { return connectionList; }
}
public List<SimergyBaseReport> GetSubReportList
{
get { return subReportList; }
}
protected List<SQLiteConnection> connectionList = new List<SQLiteConnection>();
protected List<SimergyBaseReport> subReportList = new List<SimergyBaseReport>();
public void UpdateConnectionString(String connectionString)
{
#if !RPT_DESIGN
connectionString = GlobalUtilities.GetOutputConnectionString(connectionString);
foreach (SQLiteConnection conn in connectionList)
{
conn.Close();
conn.ConnectionString = connectionString;
reportDataSet.Clear();
}
foreach (BaseReport subReport in comparisonSubReportList)
{
if (subReport != null)
subReport.UpdateConnectionString(connectionString);
}
#endif
}
}
// Report
public partial class UsageSummary : Simergy.Reporting.Reports.BaseReport
{
public ClimaticSummary()
{
InitializeComponent();
}
public ClimaticSummary(String connectionString, Model model)
: base(model)
{
InitializeComponent();
connectionList.Add(applicationUsageTableAdapter.Connection);
connectionList.Add(dailyUsage_SUMMARYTableAdapter.Connection);
subReportList.Add(xrSubreport1.ReportSource as BaseReport);
subReportList.Add(xrSubreport2.ReportSource as BaseReport);
subReportList.Add(footerSubReport.ReportSource as BaseReport);
reportDataSet = report_DataSet1;
UpdateConnectionString(connectionString);
}
}
// Custom print control to render the reports
public partial class ReportViewControl : PrintControl
{
private SimergyBaseReport CurrentReport = null;
public ReportViewControl()
{
InitializeComponent();
}
public void SetReport(SimergyBaseReport report)
{
CurrentReport = report;
if (CurrentReport != null)
{
this.PrintingSystem = report.PrintingSystem;
SetupButtonVisability();
report.CreateDocument();
report.RecreateDocumentMap();
this.PrintingSystem = report.PrintingSystem;
}
}
public void RefreshReport()
{
this.PrintingSystem = null;
this.PrintingSystem = CurrentReport.PrintingSystem;
CurrentReport.CreateDocument(true);
}
}
///Usage
BaseReport report = new UsageSummary(someConnectionString);
ReportViewControl viewer = new ReportViewControl();
viewer.SetReport(report);
form1.Controls.Add(viewer);
I have a function
public TOut PerformDbOperation<TIn, TOut>(Func<IDbConnection, TIn, TOut> method, TIn param1, bool withinTransaction)
{
IDbConnection db = GetNewDbConnection();
OpenConnection(db);
IDbTransaction transaction = null;
if (withinTransaction) {
transaction = db.BeginTransaction();
}
try {
TOut result = method(db, param1);
if (withinTransaction) {
transaction.Commit();
}
return result;
}
finally {
if (withinTransaction) {
transaction.Dispose();
}
CloseConnection(db);
}
}
Is it possible to modify this function, so it would be able to wrap any method, no matter if it's void or returning something or how many parameters it requires? Or do I need to write another wrapper every time I add new method with a different number of parameters?
You could use the command pattern.
public interface IDbOperation<out TResult>
{
TResult Result { get; }
void Execute(IDbConnection connection);
}
Classes implementing this interface can have parameters passed through the constructor
public Class1 : IDbOperation<string>
{
public Class1(int param1, double param2)
{
...
}
public string Result { get; private set; }
public void Execute(IDbConnection connection)
{
...
Result = ...;
}
}
public Class2 : IDbOperation<int>
{
public Class2(Person person)
{
...
}
public int Result { get; private set; }
public void Execute(IDbConnection connection)
{
...
Result = ...;
}
}
The wrapper method becomes
public TOut PerformDbOperation<TOut>(IDbOperation<TOut> operation, bool withinTransaction)
{
IDbConnection db = GetNewDbConnection();
OpenConnection(db);
IDbTransaction transaction = null;
if (withinTransaction) {
transaction = db.BeginTransaction();
}
try {
operation.Execute(db);
if (withinTransaction) {
transaction.Commit();
}
return operation.Result;
}
finally {
if (withinTransaction) {
transaction.Dispose();
}
CloseConnection(db);
}
}
It does not need to know the parameters of the operation.
try this :
public TOut PerformDbOperation<TOut>(Function<IDbConnection,TOut> method, bool withinTransaction)
{
IDbConnection db = GetNewDbConnection();
OpenConnection(db);
IDbTransaction transaction = null;
if (withinTransaction) {
transaction = db.BeginTransaction();
}
try {
TOut result = method(db);
if (withinTransaction) {
transaction.Commit();
}
return result;
}
finally {
if (withinTransaction) {
transaction.Dispose();
}
CloseConnection(db);
}
}
and to call the methode :
string s = PerformDbOperation((db) => yourMethode(db, param1, param2, param3));
if you also need to pass methode that doesn't return anything make an overload that uses Action<IDbConnection> instead of Function<IDbConnection,TOut>.
public void PerformDbOperation(Action<IDbConnection> method, bool withinTransaction)
{
PerformDbOperation((db) =>
{
method(db);
return 0;
}
,withinTransaction); //this will call the other overload thanks to the 'return 0' stamtement
}
Maybe it would be better if you implement your own DbConnection by applying some inheritance of an existing one. So in this case, you can have your own method that will execute some stuff, and in this method you can execute the methods of your inheritance.
I have a WinForms application that gets Car data from a Sqlite database file which is generated by a CSV file from a WebService, and the related Parts for each Car. When instanced, the Car class populates all properties from the database and gets a lot of data from a WebService that takes about 30 seconds to finish so I just call an async Task after populating the database properties and let it run asyncronously. After the web service returns all data and all work is done, the instance is saved in a List that works like an internal cache.
All works well until the user makes an operation on a Part which requires the property ParentCar to be returned while the WebService method is still running, causing a new Car to be instantiated and re polling the web service as many times the property is requested while the Car doesn't exist on the cache list. This stops as soon as the first instance finishes processing but all changes will be overwritten every time the next instances finish.
I'm struggling to find a way to ensure that a Car is only instanced one time without blocking the UI during the application life time, any ideas?
This is the code that I currently have:
public class Car
{
private List<Part> _parts = new List<Part>();
public string Id { get; private set; }
public int DbIndex
{
get { return DbClass.GetCarIndexById(Id); }
}
public ReadOnlyCollection<Part> Parts { get => _parts.AsReadOnly(); }
public Car(System.Data.SQLite.SQLiteDataReader sQLiteDataReader)
{
// Code to Load all properties from sQLiteDataReader
Task.Run(() => LongRuningWebServiceTask());
}
private async Task LongRuningWebServiceTask
{
// Long running code that will populate even more properties
SaveToInternalDb();
}
private void SaveToInternalDb()
{
if (DbIndex > -1)
DbClass.UpdateCarData(this);
else
DbClass.AddCar(this);
}
public void RelateParts(IEnumerable<Part> parts)
{
_parts.AddRange(parts);
}
~Car()
{
SaveToInternalDb();
}
}
public class Part
{
public string ParentCarId { get; private set; }
public Car ParentCar
{
get
{
Task getCar = DbClass.GetCarById(ParentCarId);
getCar.Wait();
return getCar.Result;
}
}
}
public static class DbClass
{
private static SQLiteConnection sqlConn;
private readonly string sqlFile = "pathToDbFile";
private static List<Car> CarCache = new List<Car>();
public static async Task<Car> GetCarById(string> carId, bool ignoreCache = false)
{
Car foundCar = null;
if (!ignoreCache)
foundCar = CarCache.Find(s => s.Id == carId);
if (foundCar == null)
{
try
{
string sql = string.Format("SELECT * FROM all_Cars WHERE Car = '{0}';", carId);
using (SQLiteCommand command = new SQLiteCommand(sql, sqlConn))
{
using (SQLiteDataReader reader = (SQLiteDataReader)await command.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
foundCar = new Car(reader));
}
}
}
catch (Exception e)
{
string m = e.Message;
}
if (foundCar != null)
{
var partsList = await GetPartsByCarId(carId);
if (partsList.Count > 0)
Car.RelateParts(partsList);
if (!ignoreCache)
{
if (foundCar.DbIndex == -1)
CarCache.Add(foundCar);
}
}
}
return foundCar;
}
public static async Task<List<Part>> GetPartsByCarId(string carId)
{
List<Part> foundParts = new List<Part>();
int index = GeCarIndexById(carId);
Car foundCar = index > -1 ? CarCache[index] : null;
if (foundCar != null)
foundParts = await GetPartsByCarId(carId);
return foundParts;
}
public static void InitiateSqlConnection()
{
if (sqlConn == null)
{
sqlConn = new SQLiteConnection("Data Source=" + sqlFile.FullName + ";Version=3;");
try
{
sqlConn.Open();
}
catch (Exception e)
{
var m = e.Message;
}
}
else
{
if(sqlConn.State == System.Data.ConnectionState.Broken || sqlConn.State == System.Data.ConnectionState.Closed)
sqlConn.Open();
}
}
public static int GetCarIndexById(string carId)
{
int index = -1;
for(int c = 0; c < CarCache.Count;c++)
{
if(CarCache[c].Id == carId)
{
index = c;
break;
}
}
return index;
}
public static void AddCar(Car car)
{
CarCache.Add(car);
}
public static void UpdateCarData(Car car)
{
if(car != null)
{
int index = car.DbIndex;
if(index > -1)
CarCache[index] = car;
}
}
}
Using EntityFramework, I have an auto-generated file with:
namespace Chaos.Data
{
public partial class ChaosModel : OpenAccessContext, IChaosModelUnitOfWork
{
private static string connectionStringName = #"ChaosLibraryConnection";
private static BackendConfiguration backend = GetBackendConfiguration();
private static MetadataSource metadataSource = XmlMetadataSource.FromAssemblyResource("EntitiesModel.rlinq");
public ChaosModel()
:base(connectionStringName, backend, metadataSource)
{ }
public ChaosModel(string connection)
:base(connection, backend, metadataSource)
{ }
......................
In the WCF Service, I am using:
namespace Chaos.DataService
{
[ServiceContract]
public class ChaosService
{
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
if (encountertime == null) return null;
using (var context = new ChaosModel())
{
var query = from et in context.Encountertimes
where et.Tencounter.Date == ((DateTime)encountertime).Date
select et;
var result = context.CreateDetachedCopy(query.ToList());
return result;
}
}
.............................
How can I make the WCF service on startup execute a method (once) that will return a new connection string so that I can change the calls to ChaosModel() to:
using (var context = new ChaosModel(connectionString))
(I am looking for a way to add a static constructor within the WCF service--or something better?).
(The method will determine the network I am on and construct an appropriate connection string to the network server.)
Note: I can make no changes to the auto-generated Entity file.
Use static constructor.
[ServiceContract]
public class ChaosService
{
private static string connectionString;
static ChaosService(){
connectionString = your logic...
}
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
using (var context = new ChaosModel(connectionString))
{
...
}
}
}
or eventually a singleton pattern:
public class ConnectionInfo
{
public string ConnectionString { get; private set; }
private ConnectionInfo()
{
var connectionstring = string.Empty;
//some logic
this.ConnectionString = connectionstring;
}
private static ConnectionInfo current;
public static ConnectionInfo Current {
get {
if (current != null)
current = new ConnectionInfo();
return current;
}
}
}
[OperationContract]
public IEnumerable<Encountertime> GetEncounterTimes(DateTime? encountertime)
{
using (var context = new ChaosModel(ConnectionInfo.Current.ConnectionString))
{
...
}
}
This is a basically a class library project which is somehow exposed as a WCF service. The code below is a part of the Data Access Layer. 'db' is an object of a DataContext class. To save a file, we do the following-
public static Guid SaveFile(FileDetails fileDetails)
{
System.Nullable<Guid> id = null;
SystemDataContext.UsingWrite(db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
});
return id ?? Guid.Empty;
}
Then, the below would execute-
public static void UsingWrite(Action<SoftCashCreditDBDataContext> action)
{
using (var context = new SystemDataContext())
{
try
{
action(context.Write);
}
catch (Exception ex)
{
DataAccessExceptionHandler.HandleExcetion(ex, Config.DataLayerPolicy);
}
}
}
public SystemDataContext()
{
if (_stack == null)
{
_stack = new Stack<SystemDataContext>();
this.Depth = 1;
this.Read = new SoftCashCreditDBDataContext(Config.ReadDatabaseConnection);
this.Write = new SoftCashCreditDBDataContext(Config.WriteDatabaseConnection);
}
else
{
var parent = _stack.Peek();
/// Increment level of node.
this.Depth = parent.Depth + 1;
/// Copy data context from the parent
this.Read = parent.Read;
this.Write = parent.Write;
}
_stack.Push(this);
}
public int Depth { get; private set; }
public bool IsRoot { get { return this.Depth == 1; } }
[ThreadStatic]
private static Stack<SystemDataContext> _stack = null;
public SoftCashCreditDBDataContext Read { get; private set; }
public SoftCashCreditDBDataContext Write { get; private set; }
#region IDisposable Members
public void Dispose()
{
var context = _stack.Pop();
if (context.IsRoot == true)
{
context.Read.Dispose();
context.Write.Dispose();
_stack = null;
}
}
#endregion
}
They have implemented LINQ to SQL here, and created a DBContext class. The 'SaveFileData()' method is actually part of that class, where it just calls an SP inside to save the file.
What I did not follow-
What exactly does the call to UsingWrite() do here? What is passed to the 'Action action' parameter, and what is it doing?
I understand your confusion. They use 2 delegates.
This is passed to the action parameter:
db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
}
So when UsingWrite is called, the SoftCashCreditDBDataContext delegate which was set in the Write delegate will call SaveFileData.
A simplified example to help you understand Action:
public void Main()
{
Test(x => Debug.Write(x));
}
private void Test(Action<string> testAction)
{
testAction("Bla");
}
This function will call Debug.Write with the argument x, which is a string that is passed to the test action function.