C# 使用实体框架对webapi进行单元测试
关于使用实体框架的单元测试项目,还有比这更好的例子或教程吗 在我的例子中,API项目使用实体框架文件Edmx文件,并从存储库类的Edmx文件访问表。[与codefirst或dbfirst方法不同] repo类的结构如下所示C# 使用实体框架对webapi进行单元测试,c#,entity-framework,unit-testing,C#,Entity Framework,Unit Testing,关于使用实体框架的单元测试项目,还有比这更好的例子或教程吗 在我的例子中,API项目使用实体框架文件Edmx文件,并从存储库类的Edmx文件访问表。[与codefirst或dbfirst方法不同] repo类的结构如下所示 public class AppBackendRepository { // modify the type of the db field private AppDBEntities db_context = new AppDBEntities();
public class AppBackendRepository
{
// modify the type of the db field
private AppDBEntities db_context = new AppDBEntities();
public List<Student> Get()
{
return db_context.Students.ToList();
}
}
public class StudentController
{
private static AppBackendRepository repo;
public StudentController()
{
repo = new AppBackendRepository();
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = repo.Get();
return students;
}
}
公共类AppBackendRepository
{
//修改db字段的类型
私有AppDBEntities db_context=新AppDBEntities();
公共列表Get()
{
返回db_context.Students.ToList();
}
}
公共班级学生控制员
{
私有静态AppBackendRepository回购;
公共学生控制员()
{
repo=新建AppBackendRepository();
}
公共IEnumerable GetStudents()
{
List students=repo.Get();
留学生;
}
}
如何针对这种代码体系结构编写适当的单元测试?快速的答案是:你没有。 现在,我这么说是因为我倾向于认为“单元测试”是快速的,可以用于持续集成,而“集成测试”是缓慢的测试,只在晚上运行,当然,当您使用它们时 您在这里创建的问题是您正在使用不稳定的代码 以您的方法“GetStudents()”为例。在调用此方法之前,您需要依赖于实际存在的回购协议。任何单元测试都将取决于安装的实体框架,当然,这将非常缓慢。想象一下有几百个这样的单元测试框架,你的单元测试框架现在在你的系统中成了一个严重的障碍,让人们说“它太慢了,我们不使用它” 一个更好的方法是实施 首先,定义一个接口:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
公共接口是一个默认的接口
{
IEnumerable GetStudents();
}
现在,您的类只是该契约的一个实现细节,例如:
public class StudentRepository : DbContext, IStudentRepository
{
private DbSet<Student> Students;
public IEnumerable<Student> GetStudents()
{
return Students;
}
}
public class StudentRepository:DbContext,IStudentRepository
{
私立DbSet学生;
公共IEnumerable GetStudents()
{
留学生;
}
}
在使用存储库的类中,您现在可以通过构造函数注入来注入实例,并最终得到完全可单元测试的内容:
public class StudentEnrollment
{
private readonly IStudentRepository _studentRepository;
// Inject the contract here
public StudentEnrollment(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public IEnumerable<Student> GetStudentsForClass(StudentClass studentClass)
{
return _studentRepository.GetStudents().Where(student => student.class == studentClass);
}
}
公共班级学生集合
{
私人只读是学生档案库(studentRepository);
//在这里插入合同
公共学生集合(ISTUDENTREPOSITION STUDENTREPOSITION)
{
_studentRepository=studentRepository;
}
公共IEnumerable GetStudentsForClass(StudentClass StudentClass)
{
返回_studentRepository.GetStudents(),其中(student=>student.class==studentClass);
}
}
现在,作为额外的好处,您可以对逻辑的每一个细节进行单元测试,例如:
[TestMethod]
public void GetStudentsForClass_GetStudentsThrowsException_ResultIsNull()
{
// Arrange
var mock = Mock.Create<IStudentRepository();
var badException = new Exception("I'm bad");
mock.Setup(repo => repo.GetStudents()).Throws(badException);
var someClass = new StudentClass();
var instance = new StudentEnrollment(mock.object);
// Act
var result = instance.GetStudentsForClass(studentClass);
// Assert
result.ShouldBeEmpty();
}
[TestMethod]
public void GetStudentsForClass\u GetStudentsSthrowsException\u ResultIsNull()
{
//安排
var mock=mock.Create repo.GetStudents()).Throws(badException);
var someClass=newstudentclass();
var实例=新StudentEnrollment(mock.object);
//表演
var result=instance.GetStudentsForClass(studentClass);
//断言
result.ShouldBeEmpty();
}
快速回答是:你没有。
现在,我这么说是因为我倾向于认为“单元测试”是快速的,可以用于持续集成,而“集成测试”是缓慢的测试,只在晚上运行,当然,当您使用它们时
您在这里创建的问题是您正在使用不稳定的代码
以您的方法“GetStudents()”为例。在调用此方法之前,您需要依赖于实际存在的回购协议。任何单元测试都将取决于安装的实体框架,当然,这将非常缓慢。想象一下有几百个这样的单元测试框架,你的单元测试框架现在在你的系统中成了一个严重的障碍,让人们说“它太慢了,我们不使用它”
一个更好的方法是实施
首先,定义一个接口:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
公共接口是一个默认的接口
{
IEnumerable GetStudents();
}
现在,您的类只是该契约的一个实现细节,例如:
public class StudentRepository : DbContext, IStudentRepository
{
private DbSet<Student> Students;
public IEnumerable<Student> GetStudents()
{
return Students;
}
}
public class StudentRepository:DbContext,IStudentRepository
{
私立DbSet学生;
公共IEnumerable GetStudents()
{
留学生;
}
}
在使用存储库的类中,您现在可以通过构造函数注入来注入实例,并最终得到完全可单元测试的内容:
public class StudentEnrollment
{
private readonly IStudentRepository _studentRepository;
// Inject the contract here
public StudentEnrollment(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public IEnumerable<Student> GetStudentsForClass(StudentClass studentClass)
{
return _studentRepository.GetStudents().Where(student => student.class == studentClass);
}
}
公共班级学生集合
{
私人只读是学生档案库(studentRepository);
//在这里插入合同
公共学生集合(ISTUDENTREPOSITION STUDENTREPOSITION)
{
_studentRepository=studentRepository;
}
公共IEnumerable GetStudentsForClass(StudentClass StudentClass)
{
返回_studentRepository.GetStudents(),其中(student=>student.class==studentClass);
}
}
现在,作为额外的好处,您可以对逻辑的每一个细节进行单元测试,例如:
[TestMethod]
public void GetStudentsForClass_GetStudentsThrowsException_ResultIsNull()
{
// Arrange
var mock = Mock.Create<IStudentRepository();
var badException = new Exception("I'm bad");
mock.Setup(repo => repo.GetStudents()).Throws(badException);
var someClass = new StudentClass();
var instance = new StudentEnrollment(mock.object);
// Act
var result = instance.GetStudentsForClass(studentClass);
// Assert
result.ShouldBeEmpty();
}
[TestMethod]
public void GetStudentsForClass\u GetStudentsSthrowsException\u ResultIsNull()
{
//安排
var mock=mock.Create repo.GetStudents()).Throws(badException);
var someClass=newstudentclass();
var实例=新StudentEnrollment(mock.object);
//表演
var result=instance.GetStudentsForClass(studentClass);
//断言
result.ShouldBeEmpty();
}
我认为您的所有代码都应该经过测试。通过这种方式,您可以很容易地检测到某些开发人员何时打破了预期的链。因此,我总是为存储库和控制器添加测试。在您的情况下,我将添加一个测试,确保您的控制器以正确的方式使用存储库,并且存储库以正确的方式使用EF。但是,您不应该测试EF本身。Th
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
// Only active...
return _dbContext.Students.Where(x => x.Active).ToList();
}
}
public class StudentController
{
private static IAppBackendRepository _repo;
public StudentController(IAppBackendRepository repo)
{
_repo = repo;
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = _repo.Get();
return students;
}
}
[TestMethod]
public void ShouldCallRepo()
{
// With Rhino
var mockRepo = MockRepository.GenerateStub<IAppBackendRepository>();
var expectedResult = new List<Student>();
mockRepo.Expect(x => x.Get()).Return(expectedResult);
var controller = new StudentController(mockRepo);
var actualResult = controller.GetStudents();
mockRepo.VerifyAllExpectations();
Assert.AreEqual(actualResult, expectedResult); // Possible in it's own method.
}