简体   繁体   English

使用Entity Framework 6对基本服务进行单元测试

[英]Unit Testing a Base Service with Entity Framework 6

I have a base service class that my services usually inherit from, that looks like this 我有一个通常会从中继承服务的基本服务类,如下所示

public abstract class BaseService<TEntity> 
    where TEntity : class, IBaseEntity
{
    protected DataContext _context;

    protected BaseService(DataContext context)
    {
        _context = context;
    }

    public virtual async Task<ICollection<TEntity>> GetAllAsync()
    {
        return await _context.Set<TEntity>().ToListAsync();
    }

    public virtual Task<TEntity> GetAsync(long id)
    {
        return _context.Set<TEntity>().FindAsync(id);
    }

    public virtual Task<int> AddAsync(TEntity t)
    {
        if (_context.Entry(t).State == EntityState.Detached)
        {
            _context.Set<TEntity>().Add(t);
        }

        _context.Entry(t).State = EntityState.Added;

        return _context.SaveChangesAsync();
    }

    public virtual Task<int> AddAllAsync(ICollection<TEntity> all)
    {
        foreach (var item in all)
        {
            if (_context.Entry(item).State == EntityState.Detached)
            {
                _context.Set<TEntity>().Add(item);
            }

            _context.Entry(item).State = EntityState.Added;
        }

        return _context.SaveChangesAsync();
    }

    public virtual Task<int> UpdateAsync(TEntity updated)
    {
        _context.Entry(updated).State = EntityState.Modified;

        return _context.SaveChangesAsync();
    }

    public virtual Task<int> DeleteAsync(long key)
    {
        return DeleteAsync(key, true);
    }

    public virtual async Task<int> DeleteAsync(long key, bool useFlag)
    {
        TEntity entity = await GetAsync(key);
        return await DeleteAsync(entity, useFlag);
    }

    public virtual Task<int> DeleteAsync(TEntity t)
    {
        return DeleteAsync(t, true);
    }

    public virtual Task<int> DeleteAsync(TEntity t, bool useFlag)
    {
        // check if the object uses IAuditableEntity
        IAuditableEntity auditable = t as IAuditableEntity;

        if (useFlag && auditable != null)
        {
            // flag item as deleted
            auditable.IsDeleted = true;

            return UpdateAsync(t);
        }
        else
        {
            _context.Entry(t).State = EntityState.Deleted;

            return _context.SaveChangesAsync();
        }
    }

}

Now if i need to override a method in a class that inherits from it, i can do that. 现在,如果我需要重写从其继承的类中的方法,则可以执行此操作。

My question is should these methods be tested in each unit test for each class that inherits from the BaseService class, or should i only unit test the methods that i override and have a unit test for the baseservice? 我的问题是,这些方法应该在每个单元测试中对从BaseService类继承的每个类进行测试,还是应该仅对覆盖的方法进行单元测试,并对基础服务进行单元测试? Only thing with this is, that the baseService is abstract, so in order to test it i would need to create a class that inherits from it, in order to test it. 唯一的问题是baseService是抽象的,因此为了对其进行测试,我需要创建一个从其继承的类来对其进行测试。

I am new to unit testing, so sorry if this is has an obvious answer. 我是单元测试的新手,很抱歉,如果答案很明显。

Yup

In general you do indeed want to test each descendant against the contract of the base. 通常,您确实确实想根据基础合同对每个后代进行测试。 After all any overridden method could incorporate code that invalidates/contradicts/... the base behavior expected by clients of this class hierarchy. 毕竟,任何被重写的方法都可以合并使该类层次结构的客户期望的基本行为无效/矛盾的代码。 And you don't want that to happen, so you need the early alarm system of unit tests for each of the base service's descendants. 而且您不希望这样发生,因此您需要为每个基本服务的后代提供单元测试的早期警报系统。

This is something which is also known as "contract testing". 这就是所谓的“合同测试”。

One of the ways to achieve it is to create a(n abstract) unit test class to verify the behavior of the base class and then derive (concrete) unit test classes for each of its descendants. 实现它的方法之一是创建一个(n抽象的)单元测试类以验证基类的行为,然后为其每个后代派生(具体)单元测试类。 All those concrete unit test classes should have to do is to set up the instance under test - that is an instance of the descendant class. 所有这些具体的单元测试类都需要做的就是设置被测实例-这是后代类的实例。 Inheritance should then take care of running the test methods defined in the base's unit test class against the descendant instance. 然后,继承应负责针对后代实例运行基类的单元测试类中定义的测试方法。

If the base class is abstract you can still write your unit test class against this base class. 如果基类是抽象的,您仍然可以针对该基类编写单元测试类。 By marking this unit test class abstract as well, you ensure that the test runner won't instantiate it to run its test. 通过将这个单元测试类也标记为抽象,可以确保测试运行程序不会实例化它来运行其测试。 It would only "discover and run" the concrete test classes that are derived from it to test the base's descendants. 它只会“发现并运行”从其派生的具体测试类来测试基础的后代。

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

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