我无法弄清楚如何模拟我的数据库以便对我的web api控制器进行单元测试。我发现的所有资源都是首先用于代码的EF,而不是自动生成我的上下文的数据库。
简单地说,我希望能够针对我动态创建的假数据库调用我的控制器的CRUD操作,并且正在寻找最直接的方法。
我正在尝试使用http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context将它放在一起但无法管理...
我的上下文定义为:
public partial class MyEntities : DbContext
{
public MyEntities()
: base("name=MyEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Company> Companies { get; set; }
现在我明白我需要创建一个允许模拟MyEntities的IContext,但我无法弄清楚如何组织它。
我已经尝试添加以下类,但无法弄清楚如何组织它。
public interface IContext
{
IObjectSet<Company> Companies { get; }
void SaveChanges();
}
public class EFContext: IContext
{
private readonly MyEntities _data;
public EFContext()
{
_data = new MyEntities();
}
public IObjectSet<Company> Companies
{
get
{
return _data.CreateObjectSet<Company>();
}
}
public void SaveChanges()
{
_data.SaveChanges();
}
}
示例
示例控制器我想进行单元测试,如果我可以模拟数据库进行测试,这将非常容易。
public IHttpActionResult Search([FromBody]string query)
{
var companies = CompanyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
var people = PersonRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));
List<SearchResult> results = new List<SearchResult>();
foreach(Company company in companies)
results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
foreach (Person person in people)
results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });
return Ok(results);
}
答案 0 :(得分:3)
2015年11月7日更新
所以我能看到的一些测试是:
所以首先要考虑的是,&#34;什么是依赖关系&#34;这个控制器。从这个方法我可以看到CompanyRepository和PersonRepository。这些是你想要模仿的东西。即你不在这里测试它们或它们的任何功能。您正在测试方法中的最新内容。
您需要更改控制器,以便模拟它们,例如:
public class MyController : ApiController
{
private ICompanyRepository companyRepository;
private IPersonRepository personRepository;
public MyController ()
{
companyRepository = new CompanyRepository();
personRepository = new PersonRepository();
}
public MyController (ICompanyRepository CompanyRepository, IPersonRepository PersonRepository )
{
companyRepository = CompanyRepository;
personRepository = PersonRepository;
}
}
您的代码需要引用私有存储库
public IHttpActionResult Search([FromBody]string query)
{
var companies = companyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
var people = personRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));
List<SearchResult> results = new List<SearchResult>();
foreach(Company company in companies)
results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
foreach (Person person in people)
results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });
return Ok(results);
}
您的测试看起来像(取决于您使用的测试和模拟框架)。请注意,这是非常粗略的编码,如果粘贴则无法工作!:
[TestClass]
public class MyControllerTests
{
Mock<ICompanyRepository>() mockCompanyRepository = new Mock<ICompanyRepository>()
Mock<IPersonRepository>() mockPersonRepository = new Mock<IPersonRepository>()
MyController sut;
Public MyControllerTests ()
{
// Create your "System under test" which is your MyController. Pass in your mock repositories to aid your testing.
sut = new MyController(mockCompanyRepository.Object, mockPersonRepository.Object)
}
[TestMethod]
public void SearchShouldGetCompaniesOnceWhereQueryIsNotEmpty
{
//Arrange
var expectedCompanies = new List<Company>{new Company{"Company1"}, new Company{"Company2"}};
//Setup mock that will return People list when called:
mockCompanyRepository.Setup(x => x.Get()).Returns(expectedCompanies);
mockPersonRepository.Setup(x => x.Get()).Returns(new List<Person>())
//Act
var result = sut.Search("Conway");
//Assert - check the company repository was called once and the result was as expected
mockCompanyRepository.Verify(x => x.Get(), Times.Once());
OkNegotiatedContentResult<string> conNegResult = Assert.IsType<OkNegotiatedContentResult<string>>(actionResult);
Assert.Equal("data: [{Name : "Company1"}, {Name : "Company2"}]", conNegResult.Content);
}
}
您还没有在问题中包含您的存储库的代码,但如果您使用上述样式,则应该能够遵循相同的模式。如果您的存储库Get方法没有逻辑并且只是一个传递,那么我认为您不需要针对它编写测试。如果你正在做诸如订购,过滤等等时髦的事情,那么我会说你做的。但是,您然后以与上述非常类似的方式测试存储库!
快速记录。 MyController构造函数有2个方法。一个是无参数的,另一个是你的存储库。我这样做了,因为我不知道你是否使用了IoC(控制反转或依赖注入容器)。您不需要,但它将节省您必须为您要测试的所有类编写无参数构造函数。
原始答案
我认为第一个问题是&#34;你想测试什么?&#34;。在编写测试时,它应该只测试测试目标中的功能而不是任何依赖项。所以你可能不需要连接到测试数据库来运行单元测试(除非你正在进行集成测试)。但是,您需要模拟EF DBContext。查看具有大量示例的MSDN article