2016-12-28 8 views
1

xunitとMoqをasp.netコアで使用して単体テストを書きます。これはArticleに従っていますが、エラー:Asp.netコアでMoqを使用する問題:非仮想(VBでオーバーライド可能)の無効な設定

Invalid setup on a non-virtual (overridable in VB) member: m => m.Blogs

これは私が試したものです:

[Fact] 
    public void CreateBlog() 
    { 
     var mockDbSet = new Mock<DbSet<Blog>>(); 
     var mockContext = new Mock<Context>(); 

     mockContext.Setup(m => m.Blogs).Returns(mockDbSet.Object); //In this line i got error 

     var service = new BlogController(mockContext.Object); 
     service.AddBlog("ADO.NET Blog", "adtn.com"); 

     mockDbSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
     mockContext.Verify(m => m.SaveChanges(), Times.Once()); 
    } 

は、これらは私のモデルクラスです:

public class Blog 
    { 
    public int BlogId { get; set; } 
    public string Name { get; set; } 
    public string Url { get; set; } 
    public virtual IList<Post> Posts { get; set; } 
    } 

と:

public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; } 
    public DateTime RegisterDate { get; set; } 
    public int BlogId { get; set; } 
    public virtual Blog Blog { get; set; } 
} 

と、これは私のコントローラである:

public class BlogController : Controller 
    { 
    private readonly Context _context; 
    public BlogController(Context ctx) 
    { 
     _context = ctx; 
    } 

    [HttpPost] 
    public Blog AddBlog(string name, string url) 
    { 
     var blog = new Blog() { Name = name, Url = url }; 
     _context.Blogs.Add(blog); 
     _context.SaveChanges(); 
     return blog; 
    } 
} 

UPDATE:私は、仮想コンテキストにdbsetを作るが、それは別のエラーを与える:

Can not instantiate proxy of class: EntitFrameworkCore.Models.Context

問題が何でありますか?

+1

エラーメッセージ: – Tseng

+0

私は記事が言ったことをしているので、記事が間違っていますか? – pejman

+0

コンテキストのBlogsプロパティは、仮想コンテキストである必要があります。 – Nkosi

答えて

1

Contextクラスの確認Blogsプロパティをオーバーライド

public class Context : DbContext 
{ 
    public virtual DbSet<Blog> Blogs { get; set; } 
} 

であるあなたはまた、デシベルのコンテキストを抽象化検討すべきであることを確認します。

ユニットテスト時に継承されたインターフェイスを嘲笑するので、コンテキストプロパティが仮想であるかどうかは問題になりません。

クラスは、抽象度にのみ依存し、実装の詳細には依存しないようにする必要があります。

public class BlogController : Controller { 
    private readonly IContext context; 
    public BlogController(IContext context) { 
     this.context = context; 
    } 
    //...other code 
} 

であり、分離した単位試験の間に安全に擬似できる。

[Fact] 
public void CreateBlog() { 
    //Arrange 
    var mockDbSet = new Mock<DbSet<Blog>>(); 
    var mockContext = new Mock<IContext>(); 

    mockContext.Setup(m => m.Blogs).Returns(mockDbSet.Object); 

    var service = new BlogController(mockContext.Object); 

    //Act 
    service.AddBlog("ADO.NET Blog", "adtn.com"); 

    //Assert 
    mockDbSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
    mockContext.Verify(m => m.SaveChanges(), Times.Once()); 
} 
+0

それは私に別のエラーを与えます。クラス:EntitFrameworkCore.Models.Context'それはどうか考えてください。 – pejman

+0

インターフェイスでコンテキストを分割しようとしたことはありませんでしたが(他の形式の抽象化も好きですが)、ASP.NET Core IoCは明示的な 'services.AddScpüed(provider => provider.GetService )); 'Startup.csを呼び出しますか? – Tseng

+0

@Tsengは抽象化の他の形式を好む!どういう意味ですか? – pejman

0

エラーメッセージは既に明らかです。

mockContext.Setup(m => m.Blogs) 
    .Returns(mockDbSet.Object); 

私はBlogsが仮想ではありません、あなたのDbSet<Blog>財産であると仮定します。モックは、virtualとマークされたメソッド/プロパティまたはインターフェイス上でのみ動作します。

あなたは本当にあなたのDbContextを模倣する必要はありません。代わりにDbContextをメモリ内のデータベースにバックアップして使用してください。インメモリプロバイダを使用するためにコンテキストを設定する方法については、InMemoryプロバイダの主な目的は、docsを参照してください。

var options = new DbContextOptionsBuilder<BloggingContext>() 
    .UseInMemoryDatabase(databaseName: "sompe_database_name") 
    .Options; 

var context = new AppDbContext(options); 
+0

私の更新を参照してください – pejman

+1

メモリDBに(そしてそれを効果的に統合テストに変えて)使用することをお勧めします。あなたのコードは、分離/抽象化されておらず、永続性レイヤーと密接に結びついています。そして、db関連の問題は、アプリケーションの他の部分に漏れます。それはあなたがそれに悩まされている理由です – Tseng

+0

私は彼らのコンテキストのためにインターフェイスを使用するいくつかの記事を読んで、私はそれらが好きですか? – pejman

関連する問題