2016-11-29 63 views
37

我覺得我錯過了這裏真正明顯的東西。我有類需要使用.Net Core IOptions模式(?)注入選項。當我去單元測試那個類時,我想模擬各種版本的選項來驗證類的功能。有誰知道如何在Startup類之外正確地模擬/實例化/填充IOptions?.Net核心單元測試 - 模擬IOptions <T>

下面是類的一些樣品我正在使用:

設置/選項模型

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace OptionsSample.Models 
{ 
    public class SampleOptions 
    { 
     public string FirstSetting { get; set; } 
     public int SecondSetting { get; set; } 
    } 
} 

類被測試,它使用設置:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using OptionsSample.Models 
using System.Net.Http; 
using Microsoft.Extensions.Options; 
using System.IO; 
using Microsoft.AspNetCore.Http; 
using System.Xml.Linq; 
using Newtonsoft.Json; 
using System.Dynamic; 
using Microsoft.Extensions.Logging; 

namespace OptionsSample.Repositories 
{ 
    public class SampleRepo : ISampleRepo 
    { 
     private SampleOptions _options; 
     private ILogger<AzureStorageQueuePassthru> _logger; 

     public SampleRepo(IOptions<SampleOptions> options) 
     { 
      _options = options.Value; 
     } 

     public async Task Get() 
     { 
     } 
    } 
} 

單元測試在與其他類別不同的​​裝配中:

using OptionsSample.Repositories; 
using OptionsSample.Models; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Xunit; 
using Microsoft.Extensions.Logging; 
using Microsoft.AspNetCore.Http; 
using Microsoft.Extensions.Options; 
using Microsoft.Extensions.DependencyInjection; 
using Microsoft.Extensions.Configuration; 

namespace OptionsSample.Repositories.Tests 
{ 
    public class SampleRepoTests 
    { 
     private IOptions<SampleOptions> _options; 
     private SampleRepo _sampleRepo; 


     public SampleRepoTests() 
     { 
      //Not sure how to populate IOptions<SampleOptions> here 
      _options = options; 

      _sampleRepo = new SampleRepo(_options); 
     } 
    } 
} 
+1

你能提供你試圖嘲弄塊的一個小的代碼示例?謝謝! – axlj

+0

你困惑嘲笑的意思嗎?你嘲笑一個接口並將其配置爲返回一個指定的值。對於'IOptions '你只需要模擬'Value'來返回你想要的班級 – Tseng

回答

62

您需要手動創建並填充IOptions<SampleOptions>對象。你可以通過Microsoft.Extensions.Options.Options輔助類來完成。例如:

IOptions<SampleOptions> someOptions = Options.Create<SampleOptions>(new SampleOptions()); 

可以簡化一點到:

var someOptions = Options.Create(new SampleOptions()); 

顯然,這不是因爲是非常有用的。您需要實際創建並填充SampleOptions對象並將其傳遞到Create方法中。

18

如果您打算在註釋中使用@TSeng指示的Mocking Framework,則需要在您的project.json文件中添加以下依賴項。

"Moq": "4.6.38-alpha", 

一旦被恢復的依賴性,使用MOQ框架如上所述將其分配給值是作爲創建SampleOptions類的實例一樣簡單,然後。

下面是代碼大綱的外觀。

SampleOptions app = new SampleOptions(){Title="New Website Title Mocked"}; // Sample property 
// Make sure you include using Moq; 
var mock = new Mock<IOptions<SampleOptions>>(); 
// We need to set the Value of IOptions to be the SampleOptions Class 
mock.Setup(ap => ap.Value).Returns(app); 

一旦模擬是設置,你現在可以在模擬對象傳遞給構造器作爲

SampleRepo sr = new SampleRepo(mock.Object); 

HTH。

FYI我有一個概述了Github/patvin80

4

這2種方法可以儘量避免使用最小起訂量都一個Git倉庫。 在您的測試.json配置文件中使用。一個文件用於許多測試類文件。在這種情況下使用ConfigurationBuilder將會很好。

appsetting示例。JSON

{ 
    "someService" { 
     "someProp": "someValue 
    } 
} 

設置映射類的實例:

public class SomeService 
{ 
    public SomeService(IOptions<SomeServiceConfiguration> config) 
    { 
     _config = config ?? throw new ArgumentNullException(nameof(_config)); 
    } 
} 

NUnit測試類:

public class SomeServiceConfiguration 
{ 
    public string SomeProp { get; set; } 
} 

這是需要測試的服務的實施例

[TestFixture] 
public class SomeServiceTests 
{ 

    private IOptions<SomeServiceConfiguration> _config; 
    private SomeService _service; 

    [OneTimeSetUp] 
    public void GlobalPrepare() 
    { 
     var configuration = new ConfigurationBuilder() 
      .SetBasePath(Directory.GetCurrentDirectory()) 
      .AddJsonFile("appsettings.json", false) 
      .Build(); 

     _config = Options.Create(configuration.GetSection("someService").Get<SomeServiceConfiguration>()); 
    } 

    [SetUp] 
    public void PerTestPrepare() 
    { 
     _service = new SomeService(_config); 
    } 
} 
+0

這對我很好,乾杯!不希望將Moq用於看起來如此簡單的事情,並且不想嘗試用配置設置填充我自己的選項。 – Harry

0

對於我的系統和集成測試我更喜歡在測試項目中有我的配置文件的副本/鏈接。然後我使用ConfigurationBuilder來獲取選項。

using System.Linq; 
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection; 

namespace SomeProject.Test 
{ 
public static class TestEnvironment 
{ 
    private static object configLock = new object(); 

    public static ServiceProvider ServiceProvider { get; private set; } 
    public static T GetOption<T>() 
    { 
     lock (configLock) 
     { 
      if (ServiceProvider != null) return (T)ServiceProvider.GetServices(typeof(T)).First(); 

      var builder = new ConfigurationBuilder() 
       .AddJsonFile("config/appsettings.json", optional: false, reloadOnChange: true) 
       .AddEnvironmentVariables(); 
      var configuration = builder.Build(); 
      var services = new ServiceCollection(); 
      services.AddOptions(); 

      services.Configure<ProductOptions>(configuration.GetSection("Products")); 
      services.Configure<MonitoringOptions>(configuration.GetSection("Monitoring")); 
      services.Configure<WcfServiceOptions>(configuration.GetSection("Services")); 
      ServiceProvider = services.BuildServiceProvider(); 
      return (T)ServiceProvider.GetServices(typeof(T)).First(); 
     } 
    } 
} 
} 

這樣我就可以在我的TestProject裏面使用配置。對於單元測試,我更喜歡用patvin80描述的MOQ。

0

這裏不需要模擬另一種簡單的方法,而是使用OptionsWrapper:

var myAppSettingsOptions = new MyAppSettingsOptions(); 
appSettingsOptions.MyObjects = new MyObject[]{new MyObject(){MyProp1 = "one", MyProp2 = "two", }}; 
var optionsWrapper = new OptionsWrapper<MyAppSettingsOptions>(myAppSettingsOptions); 
var myClassToTest = new MyClassToTest(optionsWrapper);