我希望在运行时动态创建一个属性,这样安装程序就可以使用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;
}
答案 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;解决问题的方法(例如,引用自定义属性进行测试的能力)。
可能有更好的方法可以做到这一点,但这对我有用......
期望的结果:
希望更多地进行我们的单元测试&#34; clean&amp; amp;无缝&#34;
实际结果:
解决方案概述:
原始存储接口:
这是我们所有存储库使用的接口。所以,我们所有的建筑商和#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
}