2016-09-08 3 views
3

をMoockを使って嘲笑するときに、SaveChanges()が0以外の値を返す方法を知っている人がいるのでしょうか。私は頻繁にSaveChanges()を使用してアイテムの数がSavedまたはUnSavedの数が予想される変更の数よりも少ないか多い場合にエラーをスローします。モックDbContext SaveChanges()メソッドは値を返しません

私の経験では、データベースへの呼び出しを行わない場合、DbContext.SaveChanges()は何もしません。イェルーンHeierとNkosiから

アップデート2016年9月8日

提案がソリューションを提供し支援してきました。私はモックに深く掘り下げて、SaveChanges()が仮想だったことに気づいたはずです。参考までに、私がやっていることをデモするために、以下のコードを追加します。ビジネスロジックがデータベースに保存されるアイテムの数に依存する場合、変更を保存するためにコールバックを定義する必要があることに注意してください。DbContext.Setup().Callback(() => { /* do something */})コールバックを使用すると、変更の保存が予想される回数を追跡し、テストでアサートすることができます。

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" 方法 "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())を使用できます。 –

答えて

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); 
関連する問題