8

我是单元测试的新手,我需要一些帮助。我首先使用代码和存储库模式。我有一个通用存储库,其中包含称为通用存储库的通用 CRUD 操作(请参阅打击)

 public abstract class GenericRepository<T> where T : class
    {
        private HolidayDatabaseContext _dataContext;
        private readonly IDbSet<T> _dbset;
        protected GenericRepository(IDbFactory databaseFactory)
        {
            DatabaseFactory = databaseFactory;
            _dbset = DataContext.Set<T>();
        }
        protected IDbFactory DatabaseFactory
        {
            get;
            private set;
        }
        protected HolidayDatabaseContext DataContext
        {
            get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
        }

        public virtual void Add(T entity)
        {
            _dbset.Add(entity);
        }
        public virtual void Update(T entity)
        {
            _dataContext.Entry(entity).State = EntityState.Modified;
        }
        public virtual void Delete(T entity)
        {
            _dbset.Remove(entity);
        }
        public virtual IEnumerable<T> Enumerable()
        {
            return _dbset.AsEnumerable<T>();
        }

        public virtual IQueryable<T> List()
        {
            return _dbset.AsQueryable<T>();
        }

        public virtual T GetSingleById(int id)
        {
            return _dbset.Find(id);
        }

        public void Save()
        {
            _dataContext.SaveChanges();
        }

    }

然后我将它继承到用户存储库中并创建了一些特定的方法。见下文

public class UserRepository : GenericRepository<User>, IUserRepository
    {
        public UserRepository(IDbFactory databaseFactory)
            : base(databaseFactory) { }


        public int HolidayEntitlement(int userId)
        {
           return HolidayEntitlement(userId, DateTime.Now);
        }
        public int HolidayEntitlement(int userId, DateTime dateTime)
        {
           //Get the User
            var user = this.GetSingleById(userId);

            //Work Total Entitlement
            int entitlement = user.BaseHolidayEntitlement;

            //Years in Service
            entitlement += (dateTime - user.EmploymentStartDate).Days / 365;

            return entitlement;



        }


         public int RemainingHolidayEntitlement(int userId)
        {
            return RemainingHolidayEntitlement(userId, DateTime.Now);
        }

         public int RemainingHolidayEntitlement(int userId, DateTime dateTime)
        {
            return int.MinValue;
        }
    }

我想对 HolidayEntitlement(int userId, DateTime dateTime) 进行单元测试,但我需要在方法中模拟 GetSingleById 部分

我把它写成一个测试,但它没有编译。

 [TestMethod]
        public void GetHolidayEntitlement25()
        {
            //How to write this Unit test


            //Setup
            var user = new User { AnnualHolidayIncrement = 1, BaseHolidayEntitlement = 25, EmploymentStartDate = new DateTime(2013, 1, 1),Id=1 };

            Mock<UserRepository> mock = new Mock<UserRepository>();
            mock.Setup(m => m.GetSingleById(1)).Returns(user);

            Assert.AreEqual(25, mock.Object.HolidayEntitlement(1));
        }

任何帮助,将不胜感激

4

2 回答 2

16

您似乎在说您只想模拟部分界面。当您开始遇到这种情况时,这表明您正在混淆您的担忧并且可能在某个地方做错了什么。

在这种情况下,您的存储库所做的不仅仅是 CRUD,因此具有多种职责(它应该只有一个,查找 SOLID 编程)您正在存储库中执行业务逻辑,它不应该存在于那里!除了简单的 CRUD 操作之外的任何东西都应该移出到业务逻辑层。即您的HolidayEntitlement方法通过应用一些逻辑来计算某些东西,因此不是 CRUD / 存储库操作!

所以......你应该做的是将业务逻辑位移到一个新类中,比如UserLogic. 在UserLogic类中,您将使用注入IUserRepository与您的存储库进行通信。在UserLogic那里你可以放置你的HolidayEntitlement方法,它会调用IUserRepository.GetSingleById. 因此,当您随后测试您的课程时,您将注入具有存根版本的UserLogic模拟,然后您将回到正确的轨道上!IUserRepositoryGetSingleById

我希望这有意义/有帮助?!

--原帖--

PS我原来的帖子说你应该模拟接口,而不是实例,所以这仍然适用,我将留在这里供参考:

你应该嘲笑IUserRepository NOT UserRepository

那是因为UserRepositoryIUserRepository. 你想说你给它一个新的实现,即你的模拟。目前您正在使用ACTUALUserRepository

于 2013-11-04T11:37:51.920 回答
7

模拟通常在您需要提供虚假依赖项时使用,在这种情况下,您似乎正在尝试模拟被测系统 (SUT),这实际上没有任何意义——实际上没有意义,因为您的测试实际上并没有告诉您任何关于UserRepository; 您所做的只是测试您是否正确设置了 Mock,这不是很有用!

您提供的测试代码似乎表明您要测试UserRepository.HolidayEntitlement

我更倾向于将这样的功能从您的 Repository 类中移出,并移到一个单独的业务逻辑类型类中。通过这种方式,您可以完全隔离地测试计算用户假期权利的逻辑,这是单元测试的主要原则。

为了测试该函数是否完成了它应该做的事情(即基于 a 的属性执行计算User),您需要确保User在该函数中操作的任何实例都是 100% 隔离并在您的控制之下 - 要么使用 a User 的 Mock 或 Fake (Stub) 实例,在这种情况下,Mocks 是一个很好的选择,因为您只需要实现 SUT 需要的依赖项部分。

所以,你可以做的是:

为用户定义接口

public interface IUser
{
  int BaseHolidayEntitlement{get;set;}
  DateTime EmploymentStartDate {get;set;}
  //other properties for a User here
}

在您的 User 类上实现它

public class User:IUser
{
   //implemement your properties here
   public int BaseHolidayEntitlement{get;set;}
   public DateTime EmploymentStartDate {get;set;}
   //and so on
}

为用户逻辑创建一个类

public class UserRules
{
  public int GetHolidayEntitlement(IUser user,DateTime dateTime)
  {
     //perform your logic here and return the result
  }
}

现在您的测试变得更加简单,甚至不需要存储库

 [TestMethod]
 public void GetHolidayEntitlement_WithBase25_Returns25()
 {
    //Arrange
    var user = new Mock<IUser>();
    //setup known, controlled property values on the mock:
    user.SetupGet(u=>u.BaseHolidayEntitlement).Returns(25);
    user.SetupGet(u=>u.EmploymentStartDate).Returns(new DateTime(2013,1,1));
    var sut = new UserRules();
    int expected = 25;
    //Act
    int actual = sut.GetHolidayEntitlement(user.Object,DateTime.UtcNow);
    //Assert
    Assert.AreEqual(expected,actual,"GetHolidayEntitlement isn't working right...");
 }
于 2013-11-04T12:06:34.413 回答