2016-10-24 3 views
0

Класс SalaryManager имеет метод с именем DoCalculation, который вызывает метод GetSum, который реализуется с использованием заводского шаблона. Метод DoCalculation также выполняет некоторую другую операцию, кроме вызова метода GetSum. Я хочу, чтобы модуль тестировал метод DoCalculation, издеваясь над вызовом GetSum(). Может ли кто-нибудь предложить лучший способ реализовать это в Moq насмехаясь. Ниже приведен пример кода,Лучший способ тестирования модуля moq C# класс библиотеки проекта

interface ICalc 
{ 
int GetSum(int a, int b); 
} 

    class NormalCalc : ICalc 
    { 
     public int GetSum(int a,int b) 
     { 
      return a + b; 
     } 
    } 
    class SumFactory 
    { 
     public static ICalc GetSumObject(int option) 
     { 
      if (option == 1) 
       return new NormalCalc(); 
      return null; 
     } 
    } 
    class SalaryManager 
    { 
     private static ICalc CalcRef = SumFactory.GetSumObject(1); 

     public int DoCalculation(int a, int b) 
     { 
      int Sum=CalcRef.GetSum(a, b); 
      //Perform some other operation 
      // 
      // 
     } 
    } 

ответ

1

Не используйте статический заводский метод. Преобразуйте заводский класс в удобный сервис/интерфейс и введите его в тестируемую систему.

public interface ISumFactory { 
    ICalc GetSumObject(int option); 
} 

public class SumFactory : ISumFactory { 
    public ICalc GetSumObject(int option) { 
     if (option == 1) 
      return new NormalCalc(); 
     return null; 
    } 
} 
public class SalaryManager { 
    private ICalc CalcRef; 
    public SalaryManager(ISumFactory factory) { 
     CalcRef = factory.GetSumObject(1); 
    } 

    public int DoCalculation(int a, int b) { 
     int Sum = CalcRef.GetSum(a, b); 
     //Perform some other operation 
     // 
     // 
     //...; 
    } 
} 

Затем издеваются над зависимостями для проверки и проверки ожиданий.

[TestClass] 
public class MyTestClass { 
    [TestMethod] 
    public void MyTestMethod() { 
     //Arrange 
     var calcMock = new Mock<ICalc>(); 
     calcMock.Setup(m => m.GetSum(It.IsAny<int>(), It.IsAny<int>())) 
      .Returns((int a, int b) => a + b) 
      .Verifiable(); 

     var factoryMock = new Mock<ISumFactory>(); 
     factoryMock.Setup(m => m.GetSumObject(1)).Returns(calcMock.Object) 
     .Verifiable(); 

     var sut = new SalaryManager(factoryMock.Object); 

     //Act 
     var result = sut.DoCalculation(1, 1); 

     //Assert 
     //... 
     factoryMock.Verify(); 
     calcMock.Verify(); 
    } 
} 
1

Для того, чтобы издеваться метод GetSum вам нужно будет вводить ICalc зависимость классу SalaryManager каким-то образом.

Хотя SalaryManager создает ICalc с использованием фабрики и сам по себе не достаточно хорош, фабричный метод сам по себе является статическим методом, который возвращает конкретный класс, и вы не можете его изменить, чтобы вернуть издеваемую класс, вместо этого является статическим методом, поэтому в соответствии с этим дизайном даже ваши модульные тесты должны будут использовать NormalCalc в качестве реализации Icalc.

У вас есть в основном 2 варианта:

  1. Inject ICalc непосредственно к SalaryManager через его конструктор:

    public class SalaryManager 
    { 
        private readonly ICalc _calc; 
    
        public SalaryManager(ICalc clac) 
        { 
         _calc = calc; 
        } 
    
        public int DoCalculation(int a, int b) 
        { 
         int Sum = _calc.GetSum(a, b); 
         //... 
        } 
    } 
    

    Теперь легко придать высмеивал экземпляр ICalc к классу, просто создайте макет ICalc, используя moq, и передайте его в calss через конструктор.

  2. Опция Anotehr, если вы все еще хотите использовать завод (который кажется довольно бесполезным в вашем случае в соответствии с тем, что он делает, в качестве побочного примечания. Я стараюсь использовать заводы при работе с DI только тогда, когда класс зависит от IDisposable объект, который я не хочу, чтобы сохранить жизнь в течение всего времени жизни в классе), чтобы изменить его от статического метода к конкретной фабрике, которая будет implememnt в интерфейсе:

    public interface ISumFactory 
    { 
        ICalc GetCalc(int option); 
    } 
    
    public SumFactory : ISumFactory 
    { 
        public ICalc GetCalc(int option) 
        { 
         if (option == 1) 
          return new NormalCalc(); 
         return null; 
        }  
    } 
    

    Теперь вы должны впрыснуть завод interafce в класс SalaryManager через свой конструктор и использовать его, когда вам это нужно:

    public class SalaryManager 
    { 
        private readonly ICalcFacotry _calcFactory; 
    
        public SalaryManager(ICalcFacotry clacFacotry) 
        { 
         _calcFactory = clacFacotry; 
        } 
    
        public int DoCalculation(int a, int b) 
        { 
         ICalc calc = _calcFactory.GetCalc(1); 
         int Sum = calc.GetSum(a, b); 
         //... 
        } 
    } 
    

    Теперь в тестовом модуле вы можете создать ICalcFacotry издеваться и передать его в свой класс, вы должны настроить издевались Facotry вернуть издеваться ICalc при использовании метода FACOTRY с 1 в качестве опции.

1

Вы можете протестировать этот метод без изменения исходного кода.
Используя Typmock Изолятор, вы сможете издеваться над будущим экземпляром класса до его создания, поэтому, когда вы издеваетесь над будущим экземпляром Iclac, он будет издеваться над всеми классами, реализующими этот интерфейс.

, например:

[TestMethod] 
public void TestMethod() 
{ 
    //mock the future instances of Iclac 
    //and when the next NormalClac will be created it will be mocked as well 
    var fakeIclac = Isolate.Fake.NextInstance<ICalc>(); 

    //setting the behavior of GetSum 
    Isolate.WhenCalled(() => fakeIclac.GetSum(0, 0)).WillReturn(5); 

    var result = new SalaryManager().DoCalculation(0, 0); 

    Assert.AreEqual(5, result); 
} 
Смежные вопросы