如何在运行时将属性附加到MOQ对象中

时间:2016-04-20 17:49:44

标签: c# unit-testing moq

我希望在运行时动态创建一个属性,这样安装程序就可以使用THAT PROPERTY来实现它的属性。数据和/或数据库值:

问题:
如何动态地将属性附加到MOCK INSTANCE?

示例:

public Mock<IRepository<tClaims>> MockClaimsRepository()
{
    var repository = new Mock<IRepository<tClaims>>();

    // HOW DO I DO THIS?
    repository.SetupProperty<Claim>(//* How do I append a dynamic property here *//)
    repository.SetupProperty<List<Claims>>(//* How do I append a dynamic property here *//)

    repository.Setup(x => x.GetActive()).Returns(repository.Claims.AsQueryable());
    repository.Setup(x => x.GetAll()).Returns(repository.Claims.AsQueryable());
    repository.Setup(x => x.GetById(repository.Claim.Claim_ID)).Returns(Claim);
    repository.Setup(x => x.Find(new object[] { It.IsAny<object>() })).Returns(repository.Claim);
    repository.Setup(x => x.Add(repository.Claim)).Verifiable();
    repository.Setup(x => x.AddRange(repository.Claims)).Verifiable();
    repository.Setup(x => x.Update(repository.Claim)).Verifiable();
    repository.Setup(x => x.Delete(repository.Claim)).Verifiable();
    repository.Setup(x => x.Delete(It.IsAny<object>())).Verifiable();

    return repository;
}

3 个答案:

答案 0 :(得分:1)

您不能直接向模拟对象添加新属性,您可以做的是使用json方法添加另一个接口的实现以及您想要的属性。

但是,如果您的目标是使用此属性为模拟提供逻辑和状态,那么您可以使用变量,如下所示:

As<>

当您从public Mock<IRepository<Claims>> MockClaimsRepository() { var repository = new Mock<IRepository<Claims>>(); var claims = new List<Claims>(); // Add variables to be used in the setups repository.Setup(x => x.GetAll()).Returns(claims.AsQueryable()); repository.Setup(x => x.GetById(It.IsAny<int>())).Returns<int>(id => claims.Find(c => c.Id == id)); repository.Setup(x => x.Add(It.IsAny<Claims>())).Callback<Claims>(c => claims.Add(c)); repository.Setup(x => x.Delete(It.IsAny<Claims>())).Callback<Claims>(c => claims.Remove(c)); ... return repository; } 返回时,这些变量不会被处理,因为您在设置中引用它。

答案 1 :(得分:0)

您可以使用SetupAllProperties()?

  

指定模拟上的所有属性应具有&#34;属性行为&#34;,   意味着设置它的值将导致它被保存   后来在请求财产时返回。 (这也是   被称为&#34; stubbing&#34;)。每个属性的默认值为   一个由mock的属性指定生成的。

只是将Arturo更新为与您的存储库一样通用:

        public Mock<IRepository<T>> MockRepository<T, TKey>() where T : BaseEntity<TKey> //(for Id )
    {
        var repository = new Mock<IRepository<T>>();

        var entities = new List<T>(); // Add variables to be used in the setups

        repository.Setup(x => x.GetAll()).Returns(entities.AsQueryable());
        repository.Setup(x => x.GetById(It.IsAny<TKey>())).Returns<TKey>(id => entities.Find(c => c.Id == id));
        repository.Setup(x => x.Add(It.IsAny<T>())).Callback<T>(c => entities.Add(c));
        repository.Setup(x => x.Delete(It.IsAny<T>())).Callback<T>(c => entities.Remove(c));
    ...
        return repository;
    }

答案 2 :(得分:0)

虽然,&#34;基本上&#34;是的,我无法将任何一个给定的答案标记为正确的原因是他们没有提供一个完整的&#34;解决问题的方法(例如,引用自定义属性进行测试的能力)。

可能有更好的方法可以做到这一点,但这对我有用......

期望的结果:

  • 模拟我的UnitOfWork(使用Moq)
  • 在我的UnitOfWork中模拟所有存储库(使用Moq)
  • 使用模拟中的属性(如数据库)中的假或伪记录
  • 从等式中获取实际的数据库问题

希望更多地进行我们的单元测试&#34; clean&amp; amp;无缝&#34;

实际结果:

  • 取得了圆满成功
  • 我们的测试非常干净,无缝且完整。易于创建(见下文)

解决方案概述:

  • 为每个存储库创建一个MockBuilder(使用Moq)
  • 为UnitOfWork创建一个MockBuilder(使用Moq)
  • 让生成器的调用消除了我们的所有复杂性

原始存储接口:
这是我们所有存储库使用的接口。所以,我们所有的建筑商和#39;每个存储库必须Mock.Setup所有这些。

public interface IRepository<TEntity> where TEntity : class
{
    #region <Methods>

    IQueryable<TEntity> GetActive();
    IQueryable<TEntity> GetAll();
    TEntity GetById(object id);
    TEntity Find(params object[] keyValues);
    void Add(TEntity entity);
    void AddRange(IEnumerable<TEntity> entities);
    void Update(TEntity entity);
    void Delete(TEntity entity);
    void Delete(object id);
    void ApplyState(TEntity entity, EntityState state);
    EntityState GetState(TEntity entity);

    #endregion
}

建筑师接口:

public interface IBuilder
{
    // Marker interface
}

存储模拟构建器接口:

public interface IRepositoryMockBuilder<TEntity> : IBuilder where TEntity : class
{
    List<TEntity> Entities { get; }

    EntityState EntityState { get; }

    Mock<IRepository<TEntity>> CreateMock();

}

CONCRETE RESOSITORY MOCK BUILDER:

public class ClaimRepositoryMockBuilder : IRepositoryMockBuilder<Claim>
{
    #region <Constructor>

    public ClaimRepositoryMockBuilder(bool autoSeed)
    {
        Entities = AutoSeed(autoSeed);
        EntityState = EntityState.Unchanged;
    }

    #endregion

    #region <Properties>

    public List<Claim> Entities { get; private set; }

    public EntityState EntityState { get; private set; }

    #endregion

    #region <Methods>

    public Mock<IRepository<Claim>> CreateMock()
    {
        var repository = new Mock<IRepository<Claim>>();

        repository.SetupAllProperties();
        repository.Setup(x => x.GetActive()).Returns(this.Entities.AsQueryable());
        repository.Setup(x => x.GetAll()).Returns(this.Entities.AsQueryable());
        repository.Setup(x => x.GetById(It.IsAny<object>())).Returns((object id) => { return this.Entities.Where(e => e.ClaimId == id.ToString()).FirstOrDefault(); });
        repository.Setup(x => x.Find(new object[] { It.IsAny<string>() })).Returns((object id) => { return this.Entities.Where(e => e.ClaimId == id.ToString()).FirstOrDefault(); });
        repository.Setup(x => x.Add(It.IsAny<Claim>())).Callback<Claim>(x => { this.Entities.Add(x); }).Verifiable();
        repository.Setup(x => x.AddRange(It.IsAny<IEnumerable<Claim>>())).Callback<IEnumerable<Claim>>(x => { this.Entities.AddRange(x); }).Verifiable();
        repository.Setup(x => x.Update(It.IsAny<Claim>())).Callback<Claim>(x => { UpdateEntity(x); }).Verifiable();
        repository.Setup(x => x.Delete(It.IsAny<Claim>())).Callback<Claim>(x => { DeleteByEntity(x); }).Verifiable();
        repository.Setup(x => x.Delete(It.IsAny<object>())).Callback<object>(x => { DeleteById(x); }).Verifiable();
        repository.Setup(x => x.ApplyState(It.IsAny<Claim>(), It.IsAny<EntityState>())).Callback<Claim, EntityState>((x, y) => { this.EntityState = y; }).Verifiable();
        repository.Setup(x => x.GetState(It.IsAny<Claim>())).Returns((Claim claim) => { return this.EntityState; });

        return repository;
    }

    #region private

    private void DeleteById(object id)
    {
        var entity = this.Entities.FirstOrDefault(x => x.ClaimId == id.ToString());
        if (entity != null)
            this.Entities.RemoveAt(Entities.IndexOf(entity));
    }

    private void DeleteByEntity(Claim deletedEntity)
    {
        var entity = this.Entities.FirstOrDefault(x => x.ClaimId == deletedEntity.ClaimId);
        if (entity != null)
            this.Entities.Remove(entity);
    }

    private void UpdateEntity(Claim updatedEntity)
    {
        var entity = this.Entities.FirstOrDefault(x => x.ClaimId == updatedEntity.ClaimId);
        if (entity != null)
            entity = updatedEntity;

    }

    private List<Claim> AutoSeed(bool autoSeed)
    {
        if (!autoSeed)
            return new List<Claim>();

        var database = new List<Claim>();
        database.Add(new Claim() 
        {
            // Set Properties Here
        });
        database.Add(new Claim()
        {
            // Set Properties Here
        });
        database.Add(new Claim()
        {
            // Set Properties Here
        });

        return database;
    }

    #endregion

    #endregion
}

CONCRETE UNIT-OF-WORK MOCK BUILDER:
添加与存储库一样多的属性(我们有TON

public class UnitOfWorkMockBuilder : IBuilder
{
    #region <Constructors>

    public UnitOfWorkMockBuilder(bool autoSeed)
    {
        ClaimsRepositoryBuilder = new ClaimsRepositoryMockBuilder(autoSeed);
        SomeOtherRepositoryBuilder = new SomeOtherRepositoryMockBuilder(autoSeed);
    }

    #endregion

    #region <Properties>

    public ClaimsRepositoryMockBuilder ClaimsRepositoryBuilder { get; set; }

    public SomeOtherRepositoryMockBuilder SomeOtherRepositoryBuilder { get; set; }

    #endregion

    #region <Methods>

    public Mock<IMyUnitOfWork> CreateMock()
    {
        var unitOfWork = new Mock<IMyUnitOfWork>();

        var depClaimTransactionsRepository = ClaimTransactionsRepositoryBuilder.CreateMock();
        var depSomeOtherRepository = SomeOtherRepository.CreateMock();

        unitOfWork.SetupAllProperties();
        unitOfWork.Object.Claim = depClaimsRepositoryBuilder.Object;
        unitOfWork.Object.SomeOther = depSomeOtherRepository.Object;

        return unitOfWork;
    }

    #endregion
}

结果单位测试:
这只是一个测试,但由于这项努力,所有的测试都被大大清理干净。

[TestClass]
public class ClaimExistsRuleUnitTest
{        
    [TestMethod]
    public void Returns_IsBau_When_Claim_DoesNotExist()
    {
        var builderUnitOfWork = new UnitOfWorkMockBuilder(true);

        var claimId = "666";
        var process = "MyAwesomeProcess";

        // -----
        // ARRANGE
        // -----
        var depUnitOfWork = builderUnitOfWork.CreateMock().Object;
        var depProcess = depUnitOfWork.Processes.GetAll().Where(x => x.FriendlyName == process).First();
        var depClaimMessage = new ClaimMessage();
        var mockValidationResult = null as IValidationResult;

        depClaimMessage.ClaimId = claimId;
        depClaimMessage.StpClaimRequestProcessId = depProcess.Stp_Process_Code_Id;

        // -----
        // ACT
        // -----
        var rule = new ClaimExistsRule();
        rule.UnitOfWork = depUnitOfWork;

        mockValidationResult = rule.Validate(depClaimMessage);

        // -----
        // ASSERT
        // -----
        Assert.AreEqual(ClaimAction.IsBAU, mockValidationResult.Action);
    }

    #endregion
}