2017-02-16 143 views
3

我是單元測試和計算器的新手。C#如何在沒有實現的情況下單元測試接口方法

我在下面的接口測試RefreshAmount

public interface IAccountService 
{ 
    double GetAccountAmount(int accountId); 
} 

這裏是取決於這個接口的類:

public class AccountObj 
{ 
    private readonly int _Id; 
    private readonly IService _service; 
    public AccountObj(int Id, IService service) 
    { 
     _Id = Id; 
     _service = service; 
    } 
    public double Amount { get; private set; } 
    public void RefreshAmount() 
    { 
     Amount = _service.GetAmount(_Id); 
    } 
} 

我的單元測試的RefreshAmount的行爲如何?

RefreshAmount致電IService.GetAmount可能會打電話給後臺辦公室,但我沒有實施。任何建議的路上將不勝感激。 (我看了一下起訂量和依賴注入,但我安靜新的單元測試)

+0

測試接口本身感覺沒有意義,因爲沒有要測試的實現。 1.編寫一個實現並用單元測試進行測試 2.正如你所提到的,你可以使用Moq來爲你的接口提供一個測試實現 –

+0

@ J.Tuc我不認爲他想測試這個接口, AccountObj'使用該接口。 –

+0

您的'AccountObj'看起來像一個實體。您應該[不要將依賴關係注入到實體的構造函數中](https://stackoverflow.com/a/28767898/264697)。 – Steven

回答

1

使用起訂量,這裏是有意見

[TestClass] 
public class AccountObjUnitTests { 
    [TestMethod] 
    public void AccountObj_Given_Id_RefreshAmount_Should_Return_Expected_Amount() { 

     //Arrange 
     //creating expected values for test 
     var expectedId = 1; 
     var expectedAmount = 100D; 
     //mock implementation of service using Moq 
     var serviceMock = new Mock<IService>(); 
     //Setup expected behavior 
     serviceMock 
      .Setup(m => m.GetAmount(expectedId))//the expected method called with provided Id 
      .Returns(expectedAmount)//If called as expected what result to return 
      .Verifiable();//expected service behavior can be verified 

     //the system under test 
     var sut = new AccountObj(expectedId, serviceMock.Object); 

     //Act 
     //exercise method under test 
     sut.RefreshAmount(); 


     //Assert 

     //verify that expectations have been met 
     serviceMock.Verify(); //verify that mocked service behaved as expected 
     Assert.AreEqual(expectedAmount, sut.Amount); 
    } 

    //Samples class and interface to explain example 
    public class AccountObj { 
     private readonly int _Id; 
     private readonly IService _service; 
     public AccountObj(int Id, IService service) { 
      _Id = Id; 
      _service = service; 
     } 
     public double Amount { get; private set; } 
     public void RefreshAmount() { 
      Amount = _service.GetAmount(_Id); 
     } 
    } 

    public interface IService { 
     double GetAmount(int accountId); 
    } 
} 

一個小例子測試這裏是相同的測試

[TestMethod] 
public void AccountInfo_RefreshAmount_Given_Id_Should_Return_Expected_Amount() { 
    //Arrange 
    //creating expected values for test 
    var expectedId = 1; 
    var expectedAmount = 100D; 
    //mock implementation of service using Moq with expected behavior 
    var serviceMock = Mock.Of<IService>(m => m.GetAmount(expectedId) == expectedAmount); 
    //the system under test 
    var sut = new AccountObj(expectedId, serviceMock); 

    //Act 
    sut.RefreshAmount();//exercise method under test 

    //Assert 
    Assert.AreEqual(expectedAmount, sut.Amount);//verify that expectations have been met 
} 
0

我不知道起訂量框架。我們使用MSTest和Microsoft Fakes這可以爲您提供存根。

然而,天真的直接的方式可以實現在測試類

public class MyTestImplementation : IAccountService 
{ 
    public bool HasBeenCalled { get; private set; } 
    public int ProvidedId { get; private set; } 
    public double Amoung { get; set; } 

    public double GetAccountAmount(int accountId) 
    { 
     HasBeenCalled = true; 
     ProvidedId = accountId; 
    } 
} 

的接口和測試方法:

[TestMethod] // or whatever attribute your test framework uses 
public void TestInterface() 
{ 
    const double EXPECTEDAMOUNT = 341; 
    const int EXPECTEDID = 42; 
    MyTestImplementation testImpl = new MyTestImplementation(); 
    testImpl.Amount = EXPECTEDAMOUNT; 

    var sut = new AccountObj(EXPECTEDID, testImpl); 

    sut.RefreshAmount(); 

    // use your assertion methods here 
    Assert.IsTrue(testImpl.HasBeenCalled); 
    Assert.AreEqual(EXPECTEDID, testImpl.ProvidedID); 
    Assert.AreEqual(EXPECTEDAMOUNT, sut.Amount); 

} 

如果你只是想檢查由此產生的Amount是正確的,你可以忽略其他屬性。通常情況下,你不想檢查RefreshAmount()如何做它應該做的,但只有當產生Amount是正確的。這種形式的東西

+0

感謝您的快速回答。因此,如果我從您的代碼中瞭解,測試即將檢查該方法是否已被有效調用,以及提供的Id是否正確。對?這不是檢查返回值是否正確。我在這裏得到這個概念嗎? –

+0

@AxelBetton哦,對,補充說,驗證。我有時候仍然感到困惑 - 我應該真的測試一下。 –

1

單元測試是關係到你的類的好作文。在這種情況下是。您可以將「服務」傳遞給您的班級,併爲您完成工作。

什麼,你需要做的是讓「testServiceClass」,將工作只是爲了測試,但不會做別的。

注:下面代碼是丟失了所有的「測試」 atributes向大家介紹的代碼

namespace MyTests 
{ 
    public class AccountObjTest 
    { 
     public void Test1() 
     { 
      int testVal = 10; 

      AccountObj obj = new AccountObj(testVal, new AccountObjTestService()); 
      obj.RefreshAmount();   
      Assert.AreEquals(obj.Amount, testVal); 
     } 
    } 

    internal class AccountObjTestService : IAccountService 
    { 
     public override double GetAmount(int accountId) 
     { 
      return accountId; 
     } 
    } 
} 

這是重要的,你的是,類本身被選中(單元測試),而不是整個實施幾個類(INTEGRATION-test)。

+0

沒錯。如果沒有注入的實現,則無法測試接口。你需要某種實現,或者模擬接口和方法。 – Sietse

0

更簡化的版本,我強烈建議你使用起訂量爲你正在努力實現。它將允許你「僞裝」你正在傳遞給你想要測試的類的任何接口的實現。這樣你一次只能測試一個類的行爲。從長遠來看,這將使單元測試變得更加容易。

創建一個測試類似乎是一個簡單的選擇,但是,如果您要處理大量場景,那麼測試類將不得不增長並且變得更復雜以迎合所有場景,並且在某個點由於其複雜性,您也必須對其進行測試。

試着學習如何使用moq,它將在長期內得到回報。您將能夠驗證傳遞給模擬對象的數據,控制模擬行爲返回的內容,測試是否調用了模擬方法以及調用了多少次。 Moq非常有用。

看一看https://github.com/Moq/moq4/wiki/Quickstart它解釋瞭如何使用起訂量得很好begginers

0

我認爲,如果你在你的代碼中的一些輸入錯誤,因爲你的界面被稱爲IAccountService而你的服務實現IService。你的班級名爲AccountObj,而你的構造函數名爲AccountInfo

因此,讓我們假設您的服務預計實施IAccountService,並且您的班級預計名爲AccountInfo

在編寫單元測試以測試AccountInfo.RefreshAmount時,您必須瞭解此功能的要求;你必須確切知道這個函數應該做什麼。

看代碼似乎的RefreshAmount的要求是:

無論實現IAccountService和任何標識用於構建AccountInfo類的對象對象,調用RefreshAmount的後置條件是財產Amount返回與IAccountService.GetAccountAmount(Id)相同的值。

或者更正式的:

  • 對於任何標識
  • 爲應調用RefreshAmount(後實現IAccountService
  • 正在使用Id和IAccountService創建的對象的任何類),返回一個值對於財產金額,等於由IAccountService.GetAccountAmount(Id)返回的值。

因爲要求說它應該適用於所有的ID和所有的AccountServices,所以你不能用所有的ID來測試你的課程。在編寫單元測試的這種情況下,你必須考慮可能發生的錯誤。

在你的例子中,它看起來沒有可能的錯誤,但是對於將來的版本,你可能會想到該函數會用不正確的ID調用服務,或者調用不正確的服務,或者忘記保存返回值財產金額或該財產金額不會返回正確的值。

您的類的要求指定它應該適用於每個Id和每個IAcocuntService。因此,您可以自由地向要測試的測試對象的構造函數提供任何Id和AccountService,這些可能會檢測到四個未來錯誤中的任何一個。

幸運的是一個簡單的AccountInfo類就足夠

class AccountService : IAccountService 
{ 
    public double GetAccountAmount(int accountId) 
    { 
     return 137 * accountId - 472; 
    } 
} 

以下單元測試的測試,該量是由提供的實現IAccountService的幾個IDS返回

空隙TestAccountObj_RefreshAmount() { 量const int Id = 438; IAccountService accountService = new AccountService(); var testObject = new AccountInfo(Id,accountService);

testObject.RefreshAmount(); 
    double amount = testObject.Amount; 
    double expectedAmount = accountService.GetAccountAmount(Id); 
    Assert.AreEqual(expectedAmount, amount); 

}

此測試將測試所有四個提到可能的錯誤。它不會找到的唯一的錯誤是,如果這個不正確的服務將返回完全相同的奇怪計算的數字,它會調用不正確的服務。這就是爲什麼我把這種奇怪的計算放在服務中,任何不正確調用的服務都不可能返回相同的錯誤。特別是如果你用不同的Ids測試使用各種TestObject

相關問題