当一个博客包含许多帖子时,如何模拟博客和帖子

时间:2016-06-16 21:24:35

标签: c# unit-testing moq entity-framework-core

说我们有

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

我想在单元测试中为BlogPost提供数据。但博客包含帖子和帖子包含博客。

[编辑]

我想将DbContext传递给控制器​​。因此,我必须模拟DbContextDbSet。对于DbSet,我需要播种一些虚拟数据。

例如:
在asp.net mvc控制器

public IActionResult GetBlog(int id)
{
    Blog blog = _context.Blog.FirstOrDefault(x => x.BlogId == id);
    return View(Blog);
}

在测试中

[Fact]
public void Get_blog_1_returns_google()
{
    // Act
    var result = _controller.GetBlog(1) as ViewResult;

    // Assert
    Assert.IsType(typeof(Blog), result.ViewData.Model);
    Blog model = (Blog)result.ViewData.Model;
    Assert.Equal("google", model.Url);
}

但我必须首先为测试提供数据。

public class BlogControllerTest
{
    private BloggingContext _context;
    private BlogController _controller;

    public BlogControllerTest()
    {
        // Seed data
        _context.Blog.Add(new Blog()
        {
            BlogId = 1,
            Url = "google",
            // how about Post
        });
        //..other code...
    }
    //...other code removed for brevity
}

如何模拟它们?

1 个答案:

答案 0 :(得分:1)

首先,您应该创建一些抽象,以使DbContext更容易模拟/测试

public interface IDbContext : IDisposable {
    int SaveChanges();
}

public interface IBloggingContext : IDbContext {
    DbSet<Blog> Blogs { get; }
    DbSet<Post> Posts { get; }
}

通过这些抽象,您可以实现具体的BloggingContext

public class BloggingContext : DbContext, IBloggingContext {
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options) { }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

假设您的BlogController正在使用构造函数注入...

public class BlogController : Controller {
    private IBloggingContext _context;

    public BlogController(IBloggingContext bloggingContext) {
        this._context = bloggingContext;
    }

    public IActionResult GetBlog(int id) {
        Blog blog = _context.Blogs.FirstOrDefault(x => x.BlogId == id);
        return View(blog);
    }
}

您现在可以使用像 Moq 这样的模拟框架来模拟IBloggingContext以返回BlogPost的虚假/模拟实例。

public class BlogControllerTest {
    private IBloggingContext _context;
    private BlogController _controller;

    public BlogControllerTest() {

        //creating seed objects
        var post = new Post() {
            PostId = 1,
            Title = "Some title",
            Content = "post contemt"
        };

        var blog = new Blog() {
            BlogId = 1,
            Url = "google",
            Posts = new List<Post>() { post }
        };

        //associating blog with post
        post.Blog = blog;
        post.BlogId = blog.BlogId;

        var blogsMock = new List<Blog>() { blog }.AsDbSetMock();
        var postsMock = new List<Post>() { post }.AsDbSetMock();

        var contextMock = new Mock<IBloggingContext>();
        contextMock.Setup(m => m.Blogs).Returns(blogsMock.Object);
        contextMock.Setup(m => m.Posts).Returns(postsMock.Object);

        _context = contextMock.Object;
        _controller = new BlogController(_context);
    }

    [Fact]
    public void Get_blog_1_returns_google() {
        // Act
        var result = _controller.GetBlog(1) as ViewResult;

        // Assert
        Assert.IsType(typeof(Blog), result.ViewData.Model);
        Blog model = (Blog)result.ViewData.Model;
        Assert.Equal("google", model.Url);
    }

}