web-dev-qa-db-fra.com

Moq et SqlConnection?

J'écris des tests unitaires pour l'un de nos produits et j'ai utilisé Moq pour simuler avec succès les connexions à Entity Framework. Cependant, je suis tombé sur la méthode suivante:

public static productValue findValues(string productName, string dbConnectionString)
{
    try
    {
        SqlConnection conn = new SqlConnection(dbConnectionString);
        conn.Open();
        //Do stuff 
    }
}

Qui accède à notre base de données à l'intérieur de cette méthode en utilisant une chaîne de connexion passée. Est-il possible de configurer une base de données fictive à l'aide de Moq et de créer une chaîne de connexion qui pointe vers la base de données fictive? J'ai essayé de faire quelque chose comme

var mockSqlConnnection = new Mock<SqlConnection>();

Bien que je ne sais pas si c'est la bonne approche, car cela se moquerait de la connexion elle-même plutôt que de la base de données.

13
Novastorm

J'avais un problème similaire.

J'ai introduit un wrapper SqlDataContext autour de SqlConnection qui a hérité de l'interface ISqlDataContext:

class SqlDataContext : ISqlDataContext {

    private readonly SqlConnection _connection;

    public SqlDataContext(string connectionString)
    {
        _connection = CreateConnection(connectionString);
    }

    public IDataReader ExecuteReader(string storedProcedureName, ICollection<SqlParameter> parameters)
    {
       // execute the command here using the _connection private field.
       // This is where your conn.Open() and "do stuff" happens.
    }

    private SqlConnection CreateConnection(string connectionString)
    {
        if (string.IsNullOrEmpty(connectionString))
        {
            throw new ArgumentNullException("connectionString");
        }

        return new SqlConnection(connectionString);
    }
}

interface ISqlDataContext
{
    IDataReader ExecuteReader(string storedProcedureName, ICollection<SqlParameter> parameters);
}

Vous pouvez ajouter des surcharges à ISqlDataContext selon vos besoins.

Cela signifie que vous pouvez ensuite simuler le ISqlDataContext comme l'exige l'utilisation de Moq ou similaire et renvoyer des valeurs de simulation.

Cela signifie que vous pouvez ensuite tester votre référentiel ou tout autre élément qui atteint la base de données via SqlConnection sans avoir à toucher la base de données.

L'autre avantage est que vous pouvez injecter ISqlContext avec DI/IoC selon vos besoins.

10
Graham

tard mais pourquoi pas avec mstest:

[TestMethod]
MyTestWithInternSqlConnection()
{
   using (ShimsContext.Create())
   {
      // simulate a connection
      ShimSqlConnection.AllInstances.Open = connection => { };
      string commandText;

      // shim-Mock all called methods
      ShimSqlCommand.AllInstances.ExecuteReader = command =>
      {
         commandText = command.CommandText;
         return new ShimSqlDataReader();
      };

      int readCount = 0;
      ShimSqlDataReader.AllInstances.Read = reader => readCount == 0;
      ShimSqlDataReader.AllInstances.GetSqlStringInt32 = (reader, i) =>
      {
         readCount++;
         return "testServer";
      };

      var theReadedString = AMethodUnderTestThatReadsFromDatabaseAString();
      Assert.IsTrue(theReadedString == "testServer");
   }
}

vous devez ajouter une référence à System.Data, puis ajouter un faux pour cela.

https://msdn.Microsoft.com/en-us/library/hh549175.aspx Mieux vaut-il, si vous changez l'implémentation et vous pouvez changer la couche de lecture utilisée mais ...

6
tire0011

Jetez un oeil à Repository Pattern , vous feriez essentiellement de la simulation des données dans vos classes consommatrices, plutôt que de vous soucier de l'implémentation de parler à la base de données.


Essentiellement, vous auriez un référentiel

namespace ContosoUniversity.DAL
{
    public class StudentRepository : IStudentRepository, IDisposable
    {
        private SchoolContext context;

        public StudentRepository(SchoolContext context)
        {
            this.context = context;
        }

        public IEnumerable<Student> GetStudents()
        {
            return context.Students.ToList();
        }

        // ... more

Qui est ensuite consommé dans d'autres classes:

   public class StudentController : Controller
   {
      private IStudentRepository studentRepository;

      public StudentController(IStudentRepository studentRepository)
      {
        this.studentRepository = studentRepository;
      }

Et utilisé comme:

  public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
  {
     var students = from s in studentRepository.GetStudents()
                    select s;

L'exemple complet se trouve dans le lien en haut.


Alors, vous passeriez un dépôt simulé dans votre classe:

// arrange
var mockedRepo = new Mock<IStudentRepository>();
// configure

// act
var controller = new StudentController(mockedRepo.Object);
// do stuff

// assert
1
NikolaiDante