执行辅助命令时Moq对象不工作



嗨,我正试图为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类来测试CommandBusinessIRepository的组合。然后您可以使用现有的测试,但通过TestRepository而不是当前的模拟IRepository,然后您的测试应该通过,因为CommandBusiness将有一个实际的存储库,它将受到Delete方法和GetAll方法的影响。

或者,不要有mock或TestRepository,只需使用真正的OrmLightRepository并为测试生成一个新的DB。这将验证一切在真实情况下是否有效。

最好的办法是在测试中配置存储库。大多数情况下,使用标准的内存集合进行存储。这意味着你的测试很快。但是你不能确定你没有使用实际数据库不支持的linq命令,所以在CI服务器上使用config将测试切换到实际数据库,并验证事情在现实生活中是否真的能正常工作。

在我看来,您正在测试Delete方法。您将需要模拟该方法需要什么,同时仍然让该方法代码做它应该做的事情。您已经设置了使用moq进行回购,以便在调用get-all时返回集合。我敢肯定,正如你所看到的,你总是会把所有的东西都拿回来的。

最新更新