繁体   English   中英

首先进行单元测试,使用LINQ进行数据库计数

[英]Unit testing first, Database Count using LINQ

由于我现在正在练习单元测试的第一个开发过程,因此在进行单元测试时遇到了这个问题。

我必须创建一个测试来验证或声明将在数据库的特定表中计数的项目。 另外,它具有参数。

  [TestMethod]
  public void verify_items_count()
  {
      //Arrange
      Mock<IRepository> Repo = new Mock<IRepository>();
      Repo.Setup(t => t.Query().Count(r => r.Id == "**WHAT SHOULD I PUT HERE IF THIS IS ONLY A MOCK**")).Returns(12);

      //Act
      //Assert
  }

如果我断言它检查它是否返回12及其参数,似乎毫无意义,因为我们都知道它不会在数据库中调用。 我将其设置为12。

请先帮助我如何编写单元测试。 我在想是否应该进行集成测试。 但是我读到一切都应该从单元测试开始。 所以我相信有办法。

不要模拟您的存储库。 而是创建一个DummyRepository来实现您的IRepository ,例如,如果您有这样的IRepository结构

public interface IRepository
{
    void Add<T>( T entity ) where T : class;
    T Get<T>( int id, params Expression<Func<T, object>>[] includes ) where T : BaseEntity;
    IQueryable<T> GetAll<T>( params Expression<Func<T, object>>[] includes ) where T : class;
    void Remove<T>( T entity ) where T : class;
    void SaveChanges();
}

那么您的DummyRepository应该这样做

public class DummyRepository : IRepository
{
    private readonly Dictionary<Type, Object> _database;

    public DummyRepository()
    {
        _database = new Dictionary<Type, object>();
    }

    public void Add<T>( T entity ) where T : class
    {
        var table = GetEntityTable<T>();

        var entityBase = entity as BaseEntity;
        if ( entityBase == null )
            throw new NotImplementedException( "You must inherit from entity base" );

        if ( entityBase.Id == 0 )
        {
            var idToUse = table.Any() ? table.Max( x => ( x as BaseEntity ).Id ) + 1 : 1;
            typeof( BaseEntity ).GetProperty( "Id" ).SetValue( entityBase, idToUse );
            table.Add( entity );
        }
        else
        {
            var itemInList = table.FirstOrDefault( x => ( x as BaseEntity ).Id == entityBase.Id );
            if ( itemInList == null )
                table.Add( entity );
            else
            {
                var itemIndex = table.IndexOf( itemInList );
                table[ itemIndex ] = entity;
            }
        }
    }


    private List<T> GetEntityTable<T>() where T : class
    {
        List<T> table;
        if ( _database.ContainsKey( typeof( T ) ) )
            table = (List<T>)_database[ typeof( T ) ];
        else
        {
            table = new List<T>();
            _database.Add( typeof( T ), table );
        }
        return table;
    }

    public T Get<T>( int id, params Expression<Func<T, object>>[] includes ) where T : BaseEntity
    {
        if ( !_database.ContainsKey( typeof( T ) ) )
            return null;
        return ( (List<T>)_database[ typeof( T ) ] ).FirstOrDefault( x => x.Id == id );
    }

    public IQueryable<T> GetAll<T>( params Expression<Func<T, object>>[] includes ) where T : class
    {
        if ( _database.ContainsKey( typeof( T ) ) )
            return ( (List<T>)_database[ typeof( T ) ] ).AsQueryable();
        return new List<T>().AsQueryable();
    }

    public void Remove<T>( T entity ) where T : class
    {
        if ( _database.ContainsKey( entity.GetType() ) )
            ( (IList)_database[ entity.GetType() ] ).Remove( entity );
    }

    public void SaveChanges() { }
}

顺便说一句,您将需要一个BaseEntity来运行此示例,如下所示

public class BaseEntity
{
    public int Id { get; set; }
}

因此,在测试开始时,您需要Add数据,并希望能够编写可以检查查询的测试。

下面的代码不会编译,因为您没有Service类,但是我很想给您一个有关如何使用它的示例。

IRepository repository = new DummyRepository();
repository.Add(new BaseEntity { Id = 1 } );
var service = new Service(repository);

幼稚的第一次测试可能是

  [TestMethod]
  public void verify_items_count()
  {
      //Arrange
      var expectedCount = 3;
      var repository = MakeRepositoryWithElements(expectedCount);

      //Act
      var actualCount = repository.Count();

      //Assert
      Assert.AreEqual(expectedCount, actualCount);
  }

MakeRepositoryWithElements(n)MakeRepositoryWithElements(n)必须设法

  • 实例化一个真实的仓库。 不是模拟或存根,因为您要测试Count()的真实实现。 这就是测试的全部内容。

  • 将n个元素放入存储库中。

教科书,冗长的做法可能是:

  1. 直接将MakeRepositoryWithElements()Count()为对真实数据库的SQL查询。

  2. 测试通过

  3. 在TDD周期的“重构”步骤中,意识到:

    • 如果要重用同一数据库作为连续测试的数据源,则针对真实数据库的测试速度很慢,并且需要清理大量数据。

    • 您的平台已经在SQL之上提供了一种抽象,可以为您进行繁重的查询,并公开对象的集合而不是数据库表。 即某种DatabaseContext 足够方便的是,数据库上下文具有一个接口(或您在其中创建了一个接口),可以实现该接口以创建自己的快速内存版本。 在测试中,您将存储库连接到该版本,而不是完整的DatabaseContext

因此,您可以进行重构,直到每个对象都严格遵守单一职责原则(存储库仅考虑对象集合,而DatabaseContext涉及基础持久性技术),并且测试运行快速且在内存中。 这些东西

public class WhateverRepository
{
  private IDatabaseContext _dbContext;

  public WhateverRepository(IDatabaseContext databaseContext) 
  {  
    _dbContext = databaseContext;
  }

  public int Count()
  {
    // This is possible because the context exposes collections of objects to us
    return _dbContext.Whatevers.Count(); 
  }
}

public WhateverRepository MakeRepositoryWithElements(int elementCount)
{
  var context = new InMemoryDatabaseContext();
  for (int i = 0; i < elementCount; i++)
  {
    context.Whatevers.Add(new Whatever());
  }
  return new WhateverRepository(context);
}

如果您是经验丰富的开发人员,或者已经在系统中实现了这样的存储库,那么您可能会直接跳转到改进的解决方案。

最后,请注意, Count()是一个简单的单行代码 ,如果数据已经以对象集合的形式公开给您,则不涉及任何逻辑,因为该平台免费为您提供Count()。 因此, 您可能希望完全跳过此测试 ,除非您仍然想通过它的动作来练习一些TDD,或者您想将该测试用作“入门”,这将使您首次分离存储库的实现。以及一些测试助手方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM