Prisoner ZERO
Prisoner ZERO

Reputation: 14176

How do I append a Property into an MOQ Object at Runtime

I am hoping to create a property dynamically at run-time so the Setup can use THAT PROPERTY for its data and/or database value(s):

QUESTION:
How can I append a property to the MOCK INSTANCE dynamically?

EXAMPLE:

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;
}

Upvotes: 1

Views: 1300

Answers (3)

Prisoner ZERO
Prisoner ZERO

Reputation: 14176

While, "basically" correct, the reason I cannot mark either of the given answers as correct is they didn't offer-up a "full" solution to the problem (e.g. the ability to reference custom properties for testing).

There may be a better way to do this, but here is what worked for me...

DESIRED OUTCOME:

  • Mock my UnitOfWork (using Moq)
  • Mock all Repositories in my UnitOfWork (using Moq)
  • Use fake-or-pseudo records from properties in the mocks (like a database)
  • Take actual database concerns OUT of the equation

The hope was to make our Unit Tests much more "clean & seamless"

ACTUAL OUTCOME:

  • It was a complete success
  • Our tests are EXTREMELY clean, seamless & easy to create (see below)

OUTLINE OF THE SOLUTION:

  • Create a MockBuilder for each Repository (using Moq)
  • Create a MockBuilder for UnitOfWork (using Moq)
  • Let calls to the Builder take away all the complexities for us

ORIGINAL REPOSITORY INTERFACE:
This is the interface all our repositories use. So, all our 'builders' had to Mock.Setup all of these for each repository.

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
}

BUILDER INTERFACE:

public interface IBuilder
{
    // Marker interface
}

REPOSITORY MOCK BUILDER INTERFACE:

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

    EntityState EntityState { get; }

    Mock<IRepository<TEntity>> CreateMock();

}

CONCRETE REPOSITORY 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:
Add as many properties as you have repositories (we have a 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
}

THE RESULTING UNIT TEST:
This is just one test, but ALL of out tests GREATLY cleaned up as a result of this effort.

[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
}

Upvotes: 0

Arnaud
Arnaud

Reputation: 84

You can use SetupAllProperties() ?

Specifies that the all properties on the mock should have "property behavior", meaning that setting its value will cause it to be saved and later returned when the property is requested. (this is also known as "stubbing"). The default value for each property will be the one generated as specified by the property for the mock.

Just to update Arturo to be generic like your repository:

        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;
    }

Upvotes: 0

Arturo Menchaca
Arturo Menchaca

Reputation: 15982

You can't directly add new properties to the mocked object, what you can do is add an implementation of another interface using As<> method with the properties you want.

But if your goal is use this properties to give logic and state to the mock, then you can use variables for that, something like this:

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;
}

These variables will not be disposed when yo return from MockClaimsRepository since you are referencing it in the setups.

Upvotes: 1

Related Questions