2017-02-09 62 views
1

我無法讓Moq模擬靜態方法中創建的對象。 這裏是我的起訂量和代碼Moq靜態類中的對象

代碼:

public interface IConfigHelper 
{ 
    string GetConfiguration(string sectionName, string elementName); 
} 

public class ConfigHelper : IConfigHelper 
{ 
    public ConfigHelper() { } 

    public virtual string GetConfiguration(string sectionName, string elementName) 
    { 
     string retValue = String.Empty; 
     //Does things to get configuration and return a value  
     return retValue; 
    } 
} 

public class myRealClass 
{ 
    public myRealClass(){} 
    public string myworkingMethod() 
    { 
     var retValue = String.Empty; 
     retValue = utilSvc.GetConfigurationValue(); 
     return retValue; 
    } 
} 

public static class utilSvc 
{ 
    public static string GetConfigurationValue() 
    { 
     ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED 
     return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem"); 
    } 
} 

使用起訂量

[TestFixture(TestName = "Tests")] 
public class Tests 
{ 
    private Mock<IConfigHelper> configHelperMOCK; 
    [SetUp] 
    public void Setup() 
    { 
     configHelperMOCK = new Mock<IConfigHelper>(); 
    } 

    [Test] 
    public void serviceIsBPManagementForValidSource() 
    { 
     //Arrange 
     string sectionName = "sectionName/sectionElement"; 
     string clinicalElementName = "ClinicalSystem"; 
     string clinicalElementValue = "Zedmed"; 
     configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue); 

     //act 
     // the call to myRealClass 

     //assert 
     // test assertions 
    } 
} 
測試

,我遇到的問題是這一行:

ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED 

我無法獲得moq來模擬物體。 我不想讓代碼讀取配置文件。我希望模仿這個實例ConfigHelper

回答

0

你不能嘲笑靜態類。我寧願建議將IConfigHelper注入myRealClass。這是如何解耦依賴關係並使用DI的常用方法。

public class myRealClass 
{ 
    private IConfigHelper _configHelper; 

    public myRealClass(IConfigHelper configHelper) 
    { 
    _configHelper = configHelper; 
    } 

    public string myworkingMethod() 
    { 
    var retValue = String.Empty; 
    retValue = _configHelper.GetConfigurationValue(); 
    return retValue; 
    } 
} 
+0

我不是想嘲笑一個靜態類!我試圖嘲笑一個靜態方法內創建的類。該方法是靜態的,因爲它是Ninject的Bind <>()使用的方法。WithConstructorArgument必須是靜態的。 – GregJF

+0

你應該仍然注入對象。同時確保你在沒有並行的情況下運行你的測試,許多測試運行者並行運行測試,當涉及靜態類時這是不安全的。 –

+0

另外,就靜態類而言,它通常可以讓它成爲一個單例,因此您仍然可以爲程序實例化實例,同時爲程序提供靜態單個實例。 –

0

避免將代碼耦合到靜態類,這在大多數情況下會導致代碼難以維護和測試。

按照Explicit Dependencies Principle

方法和類,應明確要求(通常通過 方法參數或構造函數參數)的任何協作對象,他們以正常工作需要 。

給文章一個閱讀。它短小而且信息豐富。

如果你想保留靜態類,那麼你將靜態類包裝在抽象之後。

public interface IUtilSvc { 
    string GetConfigurationValue(); 
} 

public class utilSvcWrapper : IUtilSvc { 
    public string GetConfigurationValue() { 
     return utilSvc.GetConfigurationValue(); //Calling static service 
    } 
} 

或者另一種選擇是,utlSvc不必是靜態的,如果可以注入的依賴類

public class utilSvc : IUtilScv { 
    private readonly IConfigHelper configUtil; 

    public utilSvc(IConfigHelper configHelper) { 
     configUtil = configHelper; 
    } 

    public string GetConfigurationValue() { 
     return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem"); 
    } 
} 

注入IUtilScv到相關類,以便它不再依賴於靜態類。

public class myRealClass { 
    private readonly IUtilScv utilSvc; 

    //Explicit dependency inject via constructor 
    public myRealClass(IUtilScv utilSvc) { 
     this.utilSvc = utilSvc; 
    } 

    public string myworkingMethod() { 
     var retValue = utilSvc.GetConfiguration(); 
     return retValue; 
    } 
} 

在這種情況下進行測試時,它也被抽象化了,你甚至不需要IConfigHelper。你只需要嘲笑測試所需的依賴關係。

[TestFixture(TestName = "Tests")] 
public class Tests { 
    private Mock<IUtilScv> utilScvMOCK; 

    [SetUp] 
    public void Setup() { 
     utilScvMOCK = new Mock<IUtilScv>(); 
    } 

    [Test] 
    public void serviceIsBPManagementForValidSource() { 
     //Arrange 
     var expectedClinicalElementValue = "Zedmed"; 
     utilScvMOCK 
      .Setup(s => s.GetConfiguration()) 
      .Returns(expectedClinicalElementValue) 
      .Verifiable();    

     var sut = new myRealClass(utilScvMOCK.Object); 

     //Act 
     var actualClinicalElementValue = sut.myworkingMethod(); 

     //Assert 
     configHelperMOCK.Verify(); 
     Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue); 
    } 
} 
+0

我明白,遵循並使用顯式依賴關係原則。但ConfigHelper不是myRealClass的依賴項。它依賴於utilSvc。在utilSvc的方法是靜態的,因爲它是由Ninject的綁定<>()。WithConstructorArgument,它必須是靜態的 – GregJF

+0

@GregJF檢查根據您的評論 – Nkosi

+0

更新我不能完成靜態類/方法,因爲它使用的方法必須由Ninject的Bind <>()。WithConstructorArgument調用。這需要一個靜態方法。它來自於這個靜態方法,我創建了一個讀取配置的ConfigHelper。我想嘲笑ConfigHelper – GregJF

1

您不能完成靜態類/方法,但你可以把它重定向

public static class UtilSvc 
{ 
    static UtilSvc() 
    { 
     CreatorFunc =() => new ConfigHelper(); 
    } 

    public static Func<IConfigHelper> CreatorFunc { get; set; } 

    public static string GetConfigurationValue() 
    { 
     var configUtil = CreatorFunc(); 
     return configUtil.GetConfiguration("sectionName/sectionElement", 
              "ClinicalSystem"); 
    } 
} 

,然後在測試

//... 
private Mock<IConfigHelper> configHelperMOCK; 

[SetUp] 
public void Setup() 
{ 
    configHelperMOCK = new Mock<IConfigHelper>(); 
    UtilService.CreatorFunc =() => configHelperMOCK.Object; 
} 
//... 
+0

我的對象不好。將更新。儘管對接口評論不太確定。如果UtilSvc.GetConfigurationValue()被調用,它應該返回模擬實例而不是新的實例。我錯過了什麼? – AlanT

+0

謝謝。在不在IDE中的電話上執行此操作的問題:? – AlanT

+0

是的,它應該是Func 。改變。 – AlanT