2017-05-20 1 views
0

Je suis nouveau à Moq, et j'aimerais écrire des tests unitaires en l'utilisant. J'ai une base de données avec quelques tables, comme:C#/Moq - Comment remplir avec des données de test à plusieurs niveaux?

EducationUser | Application 
- UsrName   - Student 
- UsrPwd   - CourseId 
- UsrChallenge - Date 
- IsTeacher  - Grade 
- FullName 

Ceci est une base de données sur un localdb, que je veux moquer. J'ai créé les entités en utilisant Entity Framework. L'interface de ces entités est IEducationEntities.

Maintenant, je voudrais créer un objet fantaisie et faire quelques tests de certains services Web, comme:

[TestMethod()] 
    public void LoginTest() 
    { 
     HttpResponseMessage response = Request.CreateResponse(_accountController.Login("andrew", "DefaultPassword")); 
     Assert.IsTrue(response.IsSuccessStatusCode, "User unable to log in with correct login info"); 

    } 

Pour ce faire, de ce que je l'ai compris de la documentation, je devrais pouvoir faire quelque chose comme:

[TestClass()] 
public class AccountControllerTests : ApiController 
{ 
    Mock<IEducationEntities> _entities = new Mock<IEducationEntities>(MockBehavior.Strict); 
    private AccountController _accountController; 

public AccountControllerTests() { 
     _accountController = new AccountController(_entities.Object); 
     _entities.Setup(table => table.EducationUsers.UsrName).Returns("andrew"); 
     _entities.Setup(table => table.EducationUsers.UsrPwd).Returns("DefaultPassword"); 
} 
[TestMethod] //etc, defining tests below 

Cependant, cela ne fonctionne pas du tout, étant donné que les entités générées par le databse ne contiennent pas d'informations sur les sous-zones apparemment, et je reçois l'erreur:

'DbSet' does not contain a definition for 'UsrPwd' and no extension method 'UsrPwd' accepting a first argument of type 'DbSet' could be found (are you missing a using directive or an assembly reference?)

Qu'est-ce qui me manque? Comment remplir un objet moq avec des données de test ayant la même structure que ma base de données?

+0

Dans la structure de votre table par exemple, le mot de passe est appelé 'UsrPassword', mais vous l'appelez' UsrPwd' dans votre test/code maquette. –

Répondre

2

This article describes how to mock your Entity Framework context (en supposant que vous utilisez la version 6 ou ultérieure)

que vous allez faire quelque chose comme ceci:

[TestMethod] 
public void TestSomething()  
{ 
    // Create the user data 
    var educationUsers = new List<EducationUser> 
    { 
     new EducationUser 
     { 
      UsrName = "andrew", 
      UsrPwd = "DefaultPassword" 
     } 
    }.AsQueryable(); 

    // Create the DbSet that contains the user data and wire it up to return the user data that was created above 
    Mock<DbSet<EducationUser>> educationUsersDbSet = new Mock<DbSet<EducationUser>>(); 
    educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.Provider).Returns(educationUsers.Provider); 
    educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.Expression).Returns(educationUsers.Expression); 
    educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.ElementType).Returns(educationUsers.ElementType); 
    educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.GetEnumerator()).Returns(educationUsers.GetEnumerator()); 

    // Create the mock context and wire up its EducationUsers property to return the DbSet that was created above 
    var context = new Mock<IEducationEntities>(); 
    context.Setup(e => e.EducationUsers).Returns(educationUsersDbSet.Object); 

    // Create the account controller using the mock DbContext 
    _accountController = new AccountController(context.Object); 

    // ... the rest of your testing code ... 
} 

Il va probablement obtenir ennuyeux pour configurer la maquette DbSet pour chaque type d'entité pour tous vos tests unitaires, vous pouvez donc faire une méthode pour le faire.

public static Mock<DbSet<TEntity>> CreateMockDbSet<TEntity>(IQueryable<TEntity> models) where TEntity : class 
{ 
    Mock<DbSet<TEntity>> dbSet = new Mock<DbSet<TEntity>>(); 

    dbSet.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(models.ElementType); 
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(models.Expression); 
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(models.GetEnumerator()); 
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(models.Provider); 

    return dbSet; 
} 

Ensuite, votre méthode d'essai devient

[TestMethod] 
public void TestSomething()  
{ 
    // Create the user data 
    var educationUsers = new List<EducationUser> 
    { 
     new EducationUser 
     { 
      UsrName = "andrew", 
      UsrPwd = "DefaultPassword" 
     } 
    }.AsQueryable(); 

    // Create the DbSet that contains the user data and wire it up to return the user data that was created above 
    Mock<DbSet<EducationUser>> educationUsersDbSet = new CreateMockDbSet(educationUsers); 

    // Create the mock context and wire up its EducationUsers property to return the DbSet that was created above 
    var context = new Mock<IEducationEntities>(); 
    context.Setup(e => e.EducationUsers).Returns(educationUsersDbSet.Object); 

    // Create the account controller using the mock DbContext 
    _accountController = new AccountController(context.Object); 

    // ... the rest of your testing code ... 
} 
+0

Ce qui précède était l'approche que je prendrais aussi, jusqu'à ce que j'ai commencé à implémenter le modèle async/await. Si vous lisez le lien fourni, il explique que cette approche ne fonctionne pas pour cela. En outre, l'approche qu'ils fournissent pour async/await ne fonctionne pas avec EF Core, alors soyez conscient. J'utilise maintenant UseInMemoryDatabase d'EF Core pour les tests unitaires jusqu'à la couche du référentiel avec beaucoup de succès. Si vous êtes intéressé par un exemple, je vais en fournir un. – MORCHARD

+1

Oui, j'aimerais voir un exemple. –