我有一些问题。我在我的项目中写了一些单元测试,但我不知道如何测试我的CRUD方法..也许它们不可测试; /
这是我的方法之一:
public static void IncrementInvalidLoginColumn(string login)
{
User user;
using (DTContext context = new DTContext())
{
try
{
user = context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
context.SaveChanges();
}
catch
{
}
}
}
也许有人会知道我该怎么做。
答案 0 :(得分:3)
这取决于“单位”测试的含义。如果您不希望您的测试访问数据库,那么您的方法是不可测试的(或者至少没有重构)。
如果点击数据库是可以接受的(实际上是集成测试),那么你绝对可以测试你的方法。
以下是一些步骤:
1.安排初始数据。您可以在测试中直接使用DTContext
的实例将系统置于预定义状态(基本上您在数据库中编写一些用户记录)
您运行要测试的方法(实际上使用自己的DTContext
实例)
您再次使用DTContext
直接从数据库中读取用户信息,并断言InvalidLogins
属性已增加。
您需要确保删除手动输入的任何数据。
这是DI的要点:
public class Example {
private IDatabaseGateway myDatabase;
public Example(IDatabaseGateway myDb) {
myDatabase = myDb;
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
您可以通过构造函数为您的业务类提供数据库的抽象,即您在需要它们的类中注入您的依赖项。
准备好之后,在生产代码中,您将构造函数传递给IDatabaseGateway的具体实例,该实例将转到实际数据库。
在单元测试的情况下,您传递相同接口的模拟实例。模拟是一个特殊的对象,您可以设置/配置以返回您想要的内容。存在各种用于模拟的库(一个简单的库是Moq)。
但是,如果不对代码进行过多修改,最好坚持使用可以访问数据库的集成测试。它将为您提供简单有效的测试。 特别是因为在EF中模拟DbContext存在一些缺陷(例如,当你在生产中使用它们时,某些查询可能不起作用,使用模拟测试EF中的更新有点棘手)。
答案 1 :(得分:2)
好的,所以我读了你所有的帖子,他们非常有帮助。 我使用MOQ框架,这是我如何做的例子。
这就是Liviu M.告诉我的例子:
public class CRUDclass
{
private DTContext _context;
public CRUDclass(DTContext myObj)
{
_context = myObj;
}
}
我们有CRUD类,它们直接在我们的数据库上进行操作。我们有一个带有一个参数和私有字段的构造函数。我们的背景是:)
这是(例如)我在CRUDclass中的方法:
public bool AddUser(User user)
{
try
{
_context.Users.Add(user);
_context.SaveChanges();
return true;
}
catch
{
return false;
}
}
Ovecourse他有我们的DTContext类巫婆DBSet因为我使用实体框架。之后,我能够编写一些测试方法:
[TestMethod]
public void Should_Add_User()
{
var mockSet = new Mock<DbSet<User>>();
var mockContext = new Mock<DTContext>();
mockContext.Setup(m => m.Users).Returns(mockSet.Object);
var usrCRUD = new UserCRUD(mockContext.Object);
var usr = new User();
usr.Login = "Login_Name";
usr.Email = "loginName@test.com";
usr.Password = "***";
usr.InvalidLogins = 0;
usr.RememberID = 0;
usrCRUD.AddUser(usr);
mockSet.Verify(m => m.Add(It.Is<User>(arg => arg.Login == "Login_Name")));
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
首先必须设置我的假对象(Mock&gt;)。 此测试方法检查我们的用户是否已添加到Mock :)
我希望它可以帮助某人,如果有什么不清楚请写一个问题:)
答案 2 :(得分:1)
单元测试的想法是测试你的ifs,交换机等,而不是数据库操作。 在您的情况下,您需要一个接口,它是DTContext的一个示例。在最简单的情况下,它可能如下所示。
public interface IObjectContext : IDisposable
{
IEnumerable<User> Users { get; }
}
在更复杂的情况下,您可能需要使用IQueryable<T>
或IObjectSet<T>
代替IEnumerable<T>
。
添加DTContext
的部分类声明并使其实现IObjectContext
。将构造函数添加到包含方法IncrementInvalidLoginColumn
的类,其参数类型为IObjectContext
。现在,您可以注入IObjectContext
的任何实例,而不是在您的类中创建它。此实例可以是DTContext
或模拟测试。您的课程已准备就绪,无需连接到真实数据库即可进行测试。
NB。如果是IDisposable
的实例,最好注入Func<IObjectContext>
而不是IObjectContext
。然后,您可以为每个操作创建一个实例,并在之后立即处理它。
答案 3 :(得分:1)
答案 4 :(得分:1)
理想情况下,每次调用方法时,您都会注入DTContext
而不是创建新的private readonly IDTContext _context;
public CrudClass(IDTContext context)
{
_context = context
}
。这样你就可以在单元测试中模拟该对象,并验证它是否按预期调用。
您的构造函数看起来像:
public static void IncrementInvalidLoginColumn(string login)
{
User user;
try
{
user = _context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
_context.SaveChanges();
}
catch
{
// Handle errors
}
}
您的方法现在看起来像
IDTContext
然后在您的测试中,如果您使用的是像Moq这样的框架,您基本上会编写该对象的行为方式并对其进行测试。例如,设置模拟Users
以始终为您的SaveChanges()
集合和{{1}}方法返回相同的用户,会将无效登录的数量写入您可以测试的变量。