2016-11-18 57 views
1

用几句话。 Wpf应用程序,使用。我需要使用模拟和统一来测试模型行为。统一和模拟对我来说似乎很清楚。用模拟环境提供我的模拟模型

问题如下:
模型没有通过它的构造函数获取上下文。它采用了同时执行这样的方法的背景下:

public Toys[] Get() 
{ 
    using (Context context = new Context()) 
    { 
     return context.Toys.ToArray(); 
    } 
} 

这是我尝试测试:

[TestClass] 
public class TestToyModel 
{ 
    [TestMethod] 
    public void TestToyCreate() 
    { 
     List<Toy> toys = new List<Toy>(); 
     toys.Add(new Toy{ Id = "1234", Name = "Toy1" }); 

     DbSet<Toy> dbToys = GetQueryableMockDbSet(toys); 

     Mock<ToyModel> model = new Mock<ToyModel>(); 
     Mock<Context> context = new Mock<Context>(); 
     context.Setup(x => x.Toys).Returns(dbToys); 
     //it' s all for now 
    } 

    private static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class 
    { 
     var queryable = sourceList.AsQueryable(); 

     var dbSet = new Mock<DbSet<T>>(); 
     dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider); 
     dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression); 
     dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType); 
     dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator()); 
     dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s)); 

     return dbSet.Object; 
    } 
} 

我如何可以提供我的模拟模型模拟情境?

+0

抽象上下文,以便它可以被嘲笑。提供问题的[mcve],以便评估解决方案。 – Nkosi

+0

问题不清楚。没有足够的信息提供时,不能提供明确的答案。 – Nkosi

+0

通常嘲笑你的'DbContext'不是一个好主意,因为会导致错误的安全感。在你的例子中,你正在使用'List '来模拟数据库,但是这与EF如何处理查询差距很大。例如,你可以从'Where'扩展方法调用方法,这些方法对'List '可以很好地工作,但是当使用EF时会导致运行时异常。 –

回答

1

上下文正在手动创建,因此该类与上下文紧密耦合,这使得单元测试很难单独进行。

上下文是依赖项。将其抽象出来,以便它可以被嘲笑并注入到测试中的主题中。

public interface IContext : IDisposable { 

    public DbSet<Toy> Toys { get; } 

    //...other properties and methods 
} 

public class Context : DbContext, IContext { 

    public Context() { ... } 

    public DbSet<Toy> Toys { get; set; } 

    //...other properties and methods 
} 

假设被测系统

public class ToyModel { 
    private readonly IContext context; 

    public MyClass(IContext context) { 
     this.context = context; 
    } 

    public Toys[] Get() { 
     return context.Toys.ToArray(); 
    } 

    public void Create(Toy toy) { 
     context.Toys.Add(toy); 
     context.SaveChanges(); 
    } 
} 

类不再负责创建依赖性。现在这个责任已经通过/委托给另一个班级。另外请注意,你的课程应该取决于抽象而不是结核。这在交换实现时允许更多的灵活性。像嘲笑单元测试一样。

现在上下文可以被模拟并注入到依赖类中。这是一个基于迄今为止所做工作的简单示例。

[TestClass] 
public class TestToyModel { 
    [TestMethod] 
    public void TestToyCreate() { 
     //Arrange 
     var toys = new List<Toy>(); 
     toys.Add(new Toy { Id = "1234", Name = "Toy1" }); 

     var dbToys = GetQueryableMockDbSet(toys); //DbSet<Toy> 

     var contextMock = new Mock<IContext>(); 
     contextMock.Setup(x => x.Toys).Returns(dbToys); 

     var sut = new ToyModel(contextMock.Object); 

     //Act 
     sut.Create(new Toy { Id = "5678", Name = "Toy2" }); 

     //Assert 
     var expected = 2; 
     var actual = toys.Count; 
     Assert.AreEqual(expected, actual); 
    } 

    //...other code removed for brevity 
} 

如果ToyModel是被测系统,则不需要模拟它。创建一个实例并传递模拟的依赖关系以满足测试。

+0

也许'DbSet 'IContext'中的玩具应该是'IDbSet Toys'?它会使嘲笑变得更容易。 – smoksnes

+0

它可以以任何方式工作,但根据我的经验,我通过使用IDbSet上的'DbSet'获得了更多的成功。它也取决于正在使用的EF版本。 – Nkosi

+0

@Nkosi,如果'ToyModel'具有通过名称或其他属性查询玩具的功能,您将如何测试该案例?你能否包括一个例子? –