2016-09-08 2 views
3

Мне было интересно, если бы кто-нибудь знал о способе вызвать SaveChanges(), чтобы вернуть значение, отличное от 0, при издевательстве DbContext с использованием Moq. Я часто использую SaveChanges() в своем коде, чтобы выбросить ошибки, если количество изменений элементов Сохранено или Не сохранено меньше или меньше ожидаемого числа изменений.Метод Mock DbContext SaveChanges() не возвращает никаких значений

По моему опыту использования Moq, это не похоже, что DbContext.SaveChanges() никогда ничего не сделает, если вы не совершаете вызовы в базу данных.

Update 9/8/2016

Предложения от Йерун Heier и Нкоси помогли обеспечить решение. Я должен был углубиться в Mock и понял, что SaveChanges() был виртуальным (duh!). Для справки я добавлю код ниже, чтобы продемонстрировать, что я делаю. Следует отметить, что вам необходимо определить обратный вызов для сохранения изменений, если ваша бизнес-логика зависит от количества элементов, которые сохраняются в базе данных. DbContext.Setup().Callback(() => { /* do something */}). Используя обратный вызов, вы можете отслеживать ожидаемое количество раз, когда вы вызываете изменения, и подтвердите их в своем тесте.

"SavePost" Метод в классе PostService

public PostViewModel SavePost(PostViewModel currentPost, bool publishPost) 
{ 
    //_context.Configuration.ProxyCreationEnabled = false; 

    JustBlogContext currentContext = (JustBlogContext)_Context; 
    Post newPost = new Post(); 
    List<Tag> postTags = new List<Tag>(); 
    DateTime currentDateTime = DateTime.UtcNow; 
    bool saveSuccess = false; 

    if (currentPost != null) 
    { 
     //Post 
     newPost = new Post() 
     { 
      Category = currentPost.Category.Id.Value, 
      Description = currentPost.Description, 
      ShortDescription = currentPost.ShortDescription, 
      Title = currentPost.Title, 
      UrlSlug = currentPost.UrlSlug, 
      Published = currentPost.Published == true || publishPost == true ? true : false, 
      Meta = currentPost.Meta == "" || currentPost.Meta == null ? "No meta data" : currentPost.Meta, 
      PostedOn = currentPost.PostedOn, 
      Modified = currentDateTime 
     }; 

     //Tags 
     foreach (Tag currentTag in currentPost?.Tags) 
     { 
      postTags.Add(new Tag() 
      { 
       Description = currentTag.Description, 
       Id = currentTag.Id, 
       Name = currentTag.Name, 
       UrlSlug = currentTag.UrlSlug 
      }); 
     } 

     if (currentPost.PostedOn == null && publishPost) 
     { 
      newPost.PostedOn = currentDateTime; 
     } 

     /** 
     * Note that you must track all entities 
     * from the Post side of the Post - PostTagMap - Tag database schema. 
     * If you incorrectly track entites you will add new tags as opposed to 
     * maintaining the many-to-many relationship. 
     **/ 
     if (currentPost?.Id == null) 
     { 
      //Add a new post 
      //Attach tags from the database to post 
      foreach (Tag clientTag in postTags) 
      { 
       if (currentContext.Entry(clientTag).State == EntityState.Detached) 
       { 
        currentContext.Tags.Attach(clientTag); 
       } 
      } 

      newPost.Tags = postTags; 
      currentContext.Posts.Add(newPost); 
      saveSuccess = currentContext.SaveChanges() > 0 ? true : false; 
     } 
     else 
     { 
      //Modify and existing post. 
      bool tagsModified = false; 
      newPost.Id = currentPost.Id.Value; 
      currentContext.Posts.Attach(newPost); 
      currentContext.Entry(newPost).State = EntityState.Modified; 

      saveSuccess = currentContext.SaveChanges() > 0 ? true : false; 
      List<Tag> dataTags = currentContext.Posts.Include(post => post.Tags).FirstOrDefault(p => p.Id == newPost.Id).Tags.ToList(); 

      //Remove old tags 
      foreach (Tag tag in dataTags) 
      { 
       if (!postTags.Select(p => p.Id).ToList().Contains(tag.Id)) 
       { 
        tagsModified = true; 
        newPost.Tags.Remove(tag); 
       } 
      } 

      if (postTags.Count() > 0) 
      { 
       //Add new tags 
       foreach (Tag clientTag in postTags) 
       { 
        //Attach each tag because it comes from the client, not the database 
        if (!dataTags.Select(p => p.Id).ToList().Contains(clientTag.Id)) 
        { 
         currentContext.Tags.Attach(clientTag); 
        } 

        if (!dataTags.Select(p => p.Id).ToList().Contains(clientTag.Id)) 
        { 
         tagsModified = true; 
         newPost.Tags.Add(currentContext.Tags.Find(clientTag.Id)); 
        } 
       } 

       //Only save changes if we modified the tags 
       if (tagsModified) 
       { 
        saveSuccess = currentContext.SaveChanges() > 0 ? true : false; 
       } 
      } 
     } 
    } 

    if (saveSuccess != false) 
    { 
     return loadEditedPost(currentContext, newPost); 
    } 
    else 
    { 
     throw new JustBlogException($"Error saving changes to {newPost.Title}"); 
    } 
} 

"SavePost_New_Post_Test" Метод испытаний в PostService_Test.cs

/// <summary> 
/// Test saving a new post 
/// </summary> 
[TestMethod] 
public void SavePost_New_Post_Test() 
{ 
    //Arrange 
    var postSet_Mock = new Mock<DbSet<Post>>(); 

    var justBlogContext_Mock = new Mock<JustBlogContext>(); 
    justBlogContext_Mock.Setup(m => m.Posts).Returns(postSet_Mock.Object); 

    IPostService postService = new PostService(justBlogContext_Mock.Object); 

    // setup Test 
    CategoryViewModel newCategory = new CategoryViewModel() 
    { 
     Description = "Category Description", 
     Id = 0, 
     Modified = new DateTime(), 
     Name = "Name", 
     PostCount = 0, 
     Posts = new List<Post>(), 
     UrlSlug = "Category Url Slug" 
    }; 

    Tag newTag = new Tag() 
    { 
     Description = "Tag Description", 
     Id = 1, 
     Modified = new DateTime(), 
     Name = "Tag Name", 
     Posts = new List<Post>(), 
     UrlSlug = "Url Slug" 
    }; 

    Tag newTag2 = new Tag() 
    { 
     Description = "Tag Description 2", 
     Id = 2, 
     Modified = new DateTime(), 
     Name = "Tag Name 2", 
     Posts = new List<Post>(), 
     UrlSlug = "UrlSlug2" 
    }; 

    List<Tag> tags = new List<Tag>(); 

    tags.Add(newTag); 
    tags.Add(newTag2); 

    // setup new post 
    PostViewModel newPost = new PostViewModel() 
    { 
     Category = newCategory, 
     Description = "Test Descritpion", 
     Meta = "Meta text", 
     Modified = new DateTime(), 
     Published = false, 
     PostedOn = null, 
     ShortDescription = "Short Description", 
     Title = "TestTitle", 
     UrlSlug = "TestUrlSlug", 
     Tags = tags, 
     Id = null 
    }; 

    // counters to verify call order 
    int addPostCount = 0; 
    int addTagAttachCount = 0; 
    int saveChangesCount = 0; 
    int totalActionCount = 0; 

    // Register callbacks to keep track of actions that have occured 
    justBlogContext_Mock.Setup(x => x.Posts.Add(It.IsAny<Post>())).Callback(() => 
    { 
     addPostCount++; 
     totalActionCount++; 
    }); 

    justBlogContext_Mock.Setup(x => x.SaveChanges()).Callback(() => 
    { 
     saveChangesCount++; 
     totalActionCount++; 
    }); 

    justBlogContext_Mock.Setup(x => x.SaveChanges()).Returns(1); 

    justBlogContext_Mock.Setup(x => x.Tags.Attach(It.IsAny<Tag>())).Callback(() => 
    { 
     addTagAttachCount++; 
     totalActionCount++; 
    });   

    // Act 
    postService.SavePost(newPost, false); 
    // Assert 
    justBlogContext_Mock.Verify(m => m.SaveChanges(), Times.AtLeastOnce()); 
    Assert.IsTrue(addPostCount == 1); 
    Assert.IsTrue(addTagAttachCount == 2); 
    Assert.IsTrue(totalActionCount == 4); 
} 
+1

SaveChanges - это виртуальный метод. Вы можете использовать context.Setup (x => x.SaveChanges()). Возвращает (5); –

ответ

2

Предполагая интерфейс/абстракции, как

public interface IDbContext { 
    int SaveChanges(); 
} 

Вы бы настроили макет, как это.

var expected = 3; 
var mock = new Mock<IDbContext>(); 
mock.Setup(m => m.SaveChanges()).Returns(expected); 


var context = mock.Object; 

var actual = context.SaveChanges(); 

Assert.AreEqual(expected, actual);