使用moq进行模拟,尝试将对象传递给具有多个参数的构造函数

时间:2014-02-19 05:17:08

标签: c# unit-testing moq

我正在尝试模拟一个返回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");

由于定义的构造函数不接受一个参数,如何将对象作为参数传递?请建议

2 个答案:

答案 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,但这里并没有真正相关)