c# - 用最小起订量模拟,试图将对象传递给具有多个参数的构造函数

标签 c# unit-testing moq

我正在尝试模拟一个返回 IEnumerable 数据集的方法,例如所有代码的列表。

有一个包含此方法的接口(interface) 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");

既然定义的构造函数不接受一个参数,那么如何将对象作为参数传递呢?请指教

最佳答案

如果 CodeService 正在测试中,那么您希望模拟其依赖项,而不是 CodeService 本身。

您需要为构造函数的 CodeService 的所有依赖项提供 Mock,即:

     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,但它在这里并不真正相关)

关于c# - 用最小起订量模拟,试图将对象传递给具有多个参数的构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21871678/

相关文章:

java - Apache Camel 测试

unit-testing - 函数式编程中的测试

c# - 如何使用 Moq 在没有 setter 的情况下初始化复杂属性?

c# - 异步方法返回 null

接口(interface)上的 phpUnit 代码覆盖率显示没有成功

c# - 反弹旋转边界球体

c# - 在 Windows 事件日志中检测手动时间更改和夏令时事件

c# - 阅读器关闭时调用 MetaData 的尝试无效?

entity-framework - 代码中是否有可以代替 fixture.Inject() 的 Autofixture 属性?

c# - .NET 跟踪不适用于 Diagnostics.TraceSource,仅适用于 Diagnostics.Trace