我正在尝试模拟一个返回IEnumerable数据集的方法,比如所有代码的列表。
有一个接口ISystemService.cs,它包含这个方法,一个名为SystemService.cs的服务类,它有方法定义。
受测试的系统是:
public static class CacheKeys
{
public const string ALLCURRENCYCODES = "CurrencyCodes";
}
public interface ICacheManager
{
T Get<T>(string key);
void Set(string key, object data, int cacheTime);
void Clear();
}
public interface ISessionManager
{
}
public interface IApplicationSettings
{
string LoggerName { get; }
int CacheTimeout { get; }
}
public class EFDbContext : DbContext
{
public DbSet<CurrencyCode> CurrencyCodes { get; set; }
}
public class CurrencyCode
{
public string Code { get; set; }
public string Description { get; set; }
public decimal CurrencyUnit { get; set; }
public int? DecimalPlace { get; set; }
public string BaseCurrencyCode { get; set; }
}
public interface ISystemService
{
IEnumerable<CurrencyCode> GetAllCurrencyCodes();
}
//SystemService.cs
public class SystemService : ISystemService
{
private readonly EFDbContext db;
private readonly ICacheManager cacheManager;
private readonly ISessionManager sessionManager;
private readonly IApplicationSettings appSettings;
public SystemService(EFDbContext dbContext, ICacheManager cacheManager, ISessionManager sessionManager, IApplicationSettings appSettings)
{
db = dbContext;
this.cacheManager = cacheManager;
this.sessionManager = sessionManager;
this.appSettings = appSettings;
}
public IEnumerable<CurrencyCode> GetAllCurrencyCodes()
{
var allCurrencyCodes = cacheManager.Get<IEnumerable<CurrencyCode>>(CacheKeys.ALLCURRENCYCODES);
if (allCurrencyCodes == null)
{
allCurrencyCodes = db.CurrencyCodes.ToList();
cacheManager.Set(CacheKeys.ALLCURRENCYCODES, allCurrencyCodes, appSettings.CacheTimeout);
}
return allCurrencyCodes;
}
测试方法
[TestMethod]
public void testCacheMiss()
{
List<CurrencyCode> currencycodes = new List<CurrencyCode>()
{
new CurrencyCode(){Id = 1, Code = "IND", Description = "India"},
new CurrencyCode(){Id = 2, Code = "USA", Description = "UnitedStates"},
new CurrencyCodes(){Id = 3, Code = "UAE", Description = "ArabEmirates"}
};
var mockEfContext = new Mock<EFDbContext>();
var mockCacheManager = new Mock<ICacheManager>();
var mockSessionManager = new Mock<ISessionManager>();
var mockAppSettings = new Mock<IApplicationSettings>();
// Setups for relevant methods of the above here, e.g. to test a cache miss
mockEfContext.SetupGet(x => x.CurrencyCodes)
.Returns(currencycodes); // Canned currencies
mockCacheManager.Setup(x => x.Get<IEnumerable<CurrencyCode>>(It.IsAny<string>()))
.Returns<IEnumerable<CurrencyCodes>>(null); // Cache miss
// Act
var service = new SystemService(mockEfContext.Object, mockCacheManager.Object,
mockSessionManager.Object, mockAppSettings.Object);
var codes = service.GetAllCodes();
// Assert + Verify
mockCacheManager.Verify(x => x.Get<IEnumerable<CurrencyCodes>>(
It.IsAny<string>()), Times.Once, "Must always check cache first");
mockEfContext.VerifyGet(x => x.CurrencyCodes,
Times.Once, "Because of the simulated cache miss, must go to the Db");
Assert.AreEqual(currencycodes.Count, codes.Count(), "Must return the codes as-is");
由于定义的构造函数不接受一个参数,如何将对象作为参数传递?请建议
答案 0 :(得分:1)
allCodes
是您的服务..它是您需要使用的模拟。您不应该创建ICodeService
的具体实例..您的模拟存在以填充该角色。
所以,删除它:
var service = new CodeService(allCodes.object);
你的下一行应该是:
var code = allCodes.Object.GetAllCodes();
但是那时......之后这个测试似乎完全是多余的..因为你似乎正在测试你的模拟..
此外,allCodes
应该被称为serviceMock
..因为这更有意义。
答案 1 :(得分:1)
如果CodeService
正在测试中,那么您希望模拟其依赖项,而不是CodeService
本身。
您需要为Mock
对构造函数的所有依赖项提供CodeService
,即:
var currencycodes = new List<SomeCodes>
{
new CurrencyCodes(){Id = 1, Code = "IND", Description = "India"},
new CurrencyCodes(){Id = 2, Code = "USA", Description = "UnitedStates"},
new CurrencyCodes(){Id = 3, Code = "UAE", Description = "ArabEmirates"}
};
var mockEfContext = new Mock<EFDbContext>();
var mockCacheManager = new Mock<ICacheManager>();
var mockSessionManager = new Mock<ISessionManager>();
var mockAppSettings = new Mock<IApplicationSettings>();
// Setups for relevant methods of the above here, e.g. to test a cache miss
mockEfContext.SetupGet(x => x.SomeCodes)
.Returns(currencycodes); // Canned currencies
mockCacheManager.Setup(x => x.Get<IEnumerable<SomeCodes>>(It.IsAny<string>()))
.Returns<IEnumerable<SomeCodes>>(null); // Cache miss
// Act
var service = new CodeService(mockEfContext.Object, mockCacheManager.Object,
mockSessionManager.Object, mockAppSettings.Object);
var codes = service.GetAllCodes();
// Assert + Verify
mockCacheManager.Verify(x => x.Get<IEnumerable<SomeCodes>>(
It.IsAny<string>()), Times.Once, "Must always check cache first");
mockEfContext.VerifyGet(x => x.SomeCodes,
Times.Once, "Because of the simulated cache miss, must go to the Db");
Assert.AreEqual(currencycodes.Count, codes.Count(), "Must return the codes as-is");
编辑但是,如果您的意思是代码的下一层正在测试中,则主体是相同的:
var mockCodeService = new Mock<ICodeService>();
mockCodeService.Setup(x => x.GetAllCodes())
.Returns(currencycodes); // Now we don't care whether this is from cache or db
var higherLevelClassUsingCodeService = new SomeClass(mockCodeService.Object);
higherLevelClassUsingCodeService.DoSomething();
mockCodeService.Verify(x => x.GetAllCodes(), Times.Once); // etc
编辑2
我在代码中修复了一些拼写错误,并假设CurrencyCodes
继承SomeCodes
并且您的缓存键是一个字符串,并将其推送到Git Gist here并带有相应的缓存未命中单元测试也是如此。 (我使用过NUnit,但这里并没有真正相关)