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

时间:2014-11-07 12:56:39

标签: c# design-patterns repository 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();
    }
}

当我尝试运行上面的代码时,我得到comorpiginal out作为持有7个项目,接下来我执行delete命令然后重新查询comBusiness.GetAll并再次得到7的计数,我本来预计数量为6任何想法????

2 个答案:

答案 0 :(得分:2)

CommandBusiness.GetAll()的代码执行此操作:

public List<Command> GetAll()
{
    return _repository.GetAll();
}

为存储库创建一个模拟器,它会返回一个固定列表:

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并验证预期的调用确实发生了。

恕我直言,这些测试没有提供重要的价值,因为它们确实在测试CommandBusiness的实现,而不是行为。这些测试很脆弱,每次实施更改时都需要更改。

我更喜欢一个实际与存储库交互的测试(就像你写的那个)(即使它更慢)因为这会测试行为,无论它是如何实现的。

您可以创建一个测试,通过创建一个只包含一个简单集合的CommandBusiness类来测试IRepositoryTestRepository的组合。然后你可以使用你现有的测试,但是传递你的TestRepository而不是当前的模拟IRepository然后你的测试应该通过,因为CommandBusiness将有一个实际的存储库,它将是受Delete方法以及GetAll方法影响。

或者没有模拟或TestRepository,只需使用真正的OrmLightRepository并为测试启动一个新数据库。这将验证一切在实际情况下是否真正有效。

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

答案 1 :(得分:1)

在我看来,您正在测试Delete方法。您将需要模拟该方法所需的内容,但仍然让方法代码执行它应该做的事情。你已经用moq设置了repo,以便在调用all时返回一个集合。我很确定,正如你所看到的,你将永远得到所有物品。