嗨,我正试图为CommandBusiness:类编写一个单元测试
public class CommandBusiness
{
IRepository<Command> _repository;
public CommandBusiness(IRepository<Command> repository)
{
_repository = repository;
}
public List<Command> GetCommandsFromProjectId(int id)
{
return _repository.SearchFor(command => command.ProjectId == id);
}
public List<Command> GetAll()
{
return _repository.GetAll();
}
public void Delete(int id)
{
Entities.Command command = _repository.GetById(id);
_repository.Delete(command);
}
}
public interface IRepository<T> where T : new()
{
void Delete(T entity);
List<T> SearchFor(Expression<Func<T, bool>> predicate);
List<T> GetAll();
T GetById(int id);
}
public class OrmLiteRepository<T> : IRepository<T> where T : new()
{
public IDbConnectionFactory DbFactory { get; private set; }
string ConnectionString = "";
public OrmLiteRepository()
{
DbFactory = null; // OrmLiteConnectionFactory(ConfigurationManager.AppSettings["ConnectionString"].ToString(), SqlServerDialect.Provider);
}
public List<T> SearchFor(Expression<Func<T, bool>> predicate)
{
using (IDbConnection db = DbFactory.OpenDbConnection())
{
return db.Select(predicate);
}
}
public List <T> GetAll()
{
using (IDbConnection db = DbFactory.OpenDbConnection())
{
return db.Select<T>();
}
}
public void Delete(T entity)
{
using (IDbConnection db = DbFactory.OpenDbConnection())
{
db.Delete<T>(entity);
}
}
public T GetById(int Id)
{
using (IDbConnection db = DbFactory.OpenDbConnection())
{
return db.GetById<T>(Id);
}
}
}
public class Command
{
public int Id { get; set; }
public string CommandText
{
get;
set;
}
public bool AppendToNextLine
{
get;
set;
}
public int ProjectId
{
get;
set;
}
public int SortOrder
{
get;
set;
}
}
public class Testing
{
[TestMethod]
public void TestMethod()
{
List<Command> commandsx = new List<Command>
{
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 1, ProjectId = 1 , SortOrder = 1},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 2, ProjectId = 1 , SortOrder = 2},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 3, ProjectId = 2 , SortOrder = 3},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 4, ProjectId = 2 , SortOrder = 4},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 5, ProjectId = 3 , SortOrder = 5},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 6, ProjectId = 3 , SortOrder = 6},
new Command { AppendToNextLine = false, CommandText = "SomeText", Id = 7, ProjectId = 3 , SortOrder = 7}
};
Mock<IRepository<Command>> mockProductRepository = new Mock<IRepository<Command>>();
mockProductRepository.Setup(mPR => mPR.GetAll()).Returns(commandsx);
var comBusiness = new CommandBusiness(mockProductRepository.Object);
var comsoriginal = comBusiness.GetAll();
comBusiness.Delete(3);
comsoriginal = comBusiness.GetAll();
}
}
当我试图运行上面的代码时,我得到了comsorginal,它包含7个项目,接下来我执行delete命令,然后重新查询comBusiness.GetAll,我得到的计数是7,我本来应该是6。有什么想法吗????
您为CommandBusiness.GetAll()
编写的代码执行以下操作:
public List<Command> GetAll()
{
return _repository.GetAll();
}
您为您的存储库创建了一个mock,它确实返回了一个固定列表:
Mock<IRepository<Command>> mockProductRepository = new Mock<IRepository<Command>>();
mockProductRepository.Setup(mPR => mPR.GetAll()).Returns(commandsx);
无论您呼叫CommandBusiness.GetAll()
多少次,都将始终返回固定列表。这就是模拟的作用。
实际上,您想要测试的CommandBusiness
并不是返回的实体数量是否正确,而是它与依赖项(在本例中为IRepository
)的交互是否正确。CommandBusiness
单元不负责删除命令,而是将此命令委托给IRepository
。假设存储库正确地完成了删除的工作,那么这个类的唯一功能就是在IRepository
上调用正确的方法。因此,对于一个真正的单元测试,您应该创建一个模拟IRepository
,并设置一个期望值,即将调用值为3的Delete,然后在CommandBusiness
上使用值3调用Delete,并验证预期的调用是否确实发生。
IMHO这些测试并没有提供显著的价值,因为它们真正测试的是CommandBusiness
的实现,而不是行为。这些测试很脆弱,每次实现更改时都需要进行更改。
我更喜欢一个真正与存储库交互的测试(就像你写的那个)(即使速度较慢),因为这会测试行为,无论它是如何实现的。
您可以创建一个测试,通过创建一个仅封装一个简单集合的TestRepository
类来测试CommandBusiness
和IRepository
的组合。然后您可以使用现有的测试,但通过TestRepository
而不是当前的模拟IRepository
,然后您的测试应该通过,因为CommandBusiness
将有一个实际的存储库,它将受到Delete
方法和GetAll
方法的影响。
或者,不要有mock或TestRepository,只需使用真正的OrmLightRepository并为测试生成一个新的DB。这将验证一切在真实情况下是否有效。
最好的办法是在测试中配置存储库。大多数情况下,使用标准的内存集合进行存储。这意味着你的测试很快。但是你不能确定你没有使用实际数据库不支持的linq命令,所以在CI服务器上使用config将测试切换到实际数据库,并验证事情在现实生活中是否真的能正常工作。
在我看来,您正在测试Delete方法。您将需要模拟该方法需要什么,同时仍然让该方法代码做它应该做的事情。您已经设置了使用moq进行回购,以便在调用get-all时返回集合。我敢肯定,正如你所看到的,你总是会把所有的东西都拿回来的。