Momenteel is de geteste methode te nauw gekoppeld aan implementatieproblemen om het gemakkelijk afzonderlijk testbaar te maken. Probeer die implementatieproblemen te abstraheren, zodat ze gemakkelijk kunnen worden bespot voor geïsoleerde tests.
public interface IDbConnectionFactory {
IDbConnection CreateConnection();
}
De bovenstaande verbindingsfabrieksabstractie kan worden gebruikt om toegang te krijgen tot de andere noodzakelijke System.Data
abstracties van uw MySql-gegevensopslag.
public class MyDataAccessClass {
private IDbConnectionFactory connectionFactory;
public MyDataAccessClass(IDbConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void Insert(string firstname, string lastname) {
var query = $"INSERT INTO `sakila`.`actor`(`first_name`,`last_name`) VALUES('" + firstname + "','" + lastname + "')";
Console.WriteLine(query);
using(var connection = connectionFactory.CreateConnection() {
//Creates and returns a MySqlCommand object associated with the MySqlConnection.
using(var command = connection.CreateCommand()) {
command.CommandText = query;
Console.WriteLine("Established connection");
connection.Open();
command.ExecuteNonQuery();
Console.WriteLine("Insert query succesfully executed.");
connection.Close();//is not actually necessary as the using statement will make sure to close the connection.
}
}
}
}
De productie-implementatie van de fabriek retourneert een werkelijke MySqlConnection
public class MySqlConnectionFactory: IDbConnectionFactory {
public IDbConnection CreateConnection() {
return new MySqlConnection("connection string");
}
}
die via afhankelijkheidsinjectie kan worden doorgegeven aan de gegevenslaag
Voor het testen bespot je de interfaces met behulp van je mocking-framework naar keuze of maak je je eigen vervalsingen om je methode te injecteren en te testen.
[TestClass]
public class DataAccessLayerUnitTest {
[TestMethod]
public void TestInsert() {
//Arrange
var commandMock = new Mock<IDbCommand>();
commandMock
.Setup(m => m.ExecuteNonQuery())
.Verifiable();
var connectionMock = new Mock<IDbConnection>();
connectionMock
.Setup(m => m.CreateCommand())
.Returns(commandMock.Object);
var connectionFactoryMock = new Mock<IDbConnectionFactory>();
connectionFactoryMock
.Setup(m => m.CreateConnection())
.Returns(connectionMock.Object);
var sut = new MyDataAccessClass(connectionFactoryMock.Object);
var firstName = "John";
var lastName = "Doe";
//Act
sut.Insert(firstName, lastName);
//Assert
commandMock.Verify();
}
}
Ten slotte is het raadzaam om opdrachtparameters in de opdrachttekst te gebruiken, aangezien het handmatig construeren van de queryreeks de code opent voor SQL-injectieaanvallen.
Om beter te begrijpen hoe Moq te gebruiken, raadpleeg hun Quickstart