Тестирование и издевательствование частных/защищенных методов. Многие сообщения, но все же не могут сделать один пример работы

Я видел много сообщений и вопросов о "Mocking private method", но все еще не могу заставить его работать и не нашел реального ответа. Давайте забудем запах кода, и вы не должны этого делать и т.д.

Из того, что я понимаю, я сделал следующее:

1) Создал библиотеку классов "MyMoqSamples"

2) Добавлена ​​ссылка на Moq и NUnit

3) Отредактировал файл AssemblyInfo и добавил     [сборка: InternalsVisibleTo ( "DynamicProxyGenAssembly2" )]     [сборка: InternalsVisibleTo ( "MyMoqSamples" )]

4) Теперь нужно протестировать закрытый метод. Поскольку это частный метод, он не является частью интерфейса.

5) добавлен следующий код

[TestFixture]
public class Can_test_my_private_method
{
    [Test]
    public void Should_be_able_to_test_my_private_method()
    {
        // TODO how do I test my DoSomthing method?
    }
}

public class CustomerInfo
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

public interface ICustomerService
{
    List<CustomerInfo> GetCustomers();
}

public class CustomerService : ICustomerService
{
    public List<CustomerInfo> GetCustomers()
    {
        return new List<CustomerInfo> { new CustomerInfo { Surname = "Bloggs", Name = "Jo" } };
    }

    protected virtual void DoSomething()
    {
    }
}

Не могли бы вы привести мне пример того, как вы будете тестировать мой частный метод? Большое спасибо

Ответ 1

Шаги, которые вы описываете, устанавливают Moq для проверки внутренних классов и членов, поэтому не имеют ничего общего с проверкой защищенного или закрытого метода

Тестирование частных методов - это немного запах, вы должны действительно проверить только публичный API. Если вы считаете, что этот метод действительно важен и его нужно тестировать изолированно, возможно, он заслуживает того, чтобы быть в своем классе, где он может быть протестирован сам по себе?

Если ваше сердце настроено на тестирование защищенного метода выше, вы можете катить свой собственный Mock в своей тестовой сборке:

public class CustomerServiceMock : CustomerService {
    public void DoSomethingTester() {
         // Set up state or whatever you need
         DoSomething();
    }

}

[TestMethod]
public void DoSomething_WhenCalled_DoesSomething() {
    CustomerServiceMock serviceMock = new CustomerServiceMock(...);
    serviceMock.DoSomethingTester();
 }

Если это было частным, вы, вероятно, могли бы сделать что-то хитрое с отражением, но этот маршрут - путь к тестированию ада.


Update

Пока вы задали образец кода в своем вопросе, я действительно не вижу, как вы хотите "протестировать" защищенный метод, чтобы я придумал что-то надуманное...

Допустим, ваше обслуживание клиентов выглядит так: -

 public CustomerService : ICustomerService {

      private readonly ICustomerRepository _repository;

      public CustomerService(ICustomerRepository repository) {
           _repository = repository;
      } 

      public void MakeCustomerPreferred(Customer preferred) {
           MakePreferred(customer);
           _repository.Save(customer);
      }

      protected virtual void MakePreferred(Customer customer) {
          // Or more than likely some grungy logic
          customer.IsPreferred = true;
      }
 }

Если вы хотите протестировать защищенный метод, вы можете просто сделать что-то вроде: -

[TestClass]
public class CustomerServiceTests {

     CustomerServiceTester customerService;
     Mock<ICustomerRepository> customerRepositoryMock;

     [TestInitialize]
     public void Setup() {
          customerRepoMock = new Mock<ICustomerRepository>();
          customerService = new CustomerServiceTester(customerRepoMock.Object);
     }


     public class CustomerServiceTester : CustomerService {    
          public void MakePreferredTest(Customer customer) {
              MakePreferred(customer);
          }

          // You could also add in test specific instrumentation
          // by overriding MakePreferred here like so...

          protected override void MakePreferred(Customer customer) {
              CustomerArgument = customer;
              WasCalled = true;
              base.MakePreferred(customer);
          }

          public Customer CustomerArgument { get; set; }
          public bool WasCalled { get; set; }
     }

     [TestMethod]
     public void MakePreferred_WithValidCustomer_MakesCustomerPreferred() {
         Customer customer = new Customer();
         customerService.MakePreferredTest(customer);
         Assert.AreEqual(true, customer.IsPreferred);
     }

     // Rest of your tests
}

Название этого "шаблона" - это тестовый конкретный подкласс (на основе терминологии тестовых шаблонов xUnit) для получения дополнительной информации, которую вы можете увидеть здесь: -

http://xunitpatterns.com/Test-Specific%20Subclass.html

Основываясь на ваших комментариях и предыдущем вопросе, кажется, что вам было поручено внедрить модульные тесты на некоторые устаревшие коды (или сами приняли решение). В этом случае библейский код устаревшего кода - это книга Майкла Перса. Он охватывает такие методы, как рефакторинг и методы, позволяющие разбить "непроверяемые" классы и методы на нечто более управляемое, и я настоятельно рекомендую его.

Ответ 2

Кажется, что на ваш вопрос две части.

  • Как мне высмеять защищенный метод:

    http://blogs.clariusconsulting.net/kzu/mocking-protected-members-with-moq/

  • как я могу вызвать вызов этого защищенного/частного поведения в моем тесте

    Ответ здесь заключается в том, что вы запускаете его через что-то публичное. Если вы хотите заставить его действовать напрямую (т.е. Фактически вызывать что-то protected напрямую без промежуточного помощника, вам нужно будет использовать отражение. Это по дизайну - языки предоставляют механизмы защиты как способ обеспечения инкапсуляции.

Я предлагаю вам подумать о том, что вы пытаетесь продемонстрировать/доказать в своем тесте. Если вы обнаруживаете, что пишете сложный тест, вы делаете это неправильно. Возможно, что вы хотите сделать, можно разделить на независимые тесты? Возможно, вы пишете интеграционный тест, а не unit test? Но есть много статей о плохих тестах, и более того, это будет иметь смысл для вас, когда вы научитесь писать хорошие тесты. Два из моих фаворитов: http://www.codethinked.com/post/2009/06/30/What-is-Unit-Testing.aspx и http://www.codethinked.com/post/2009/11/05/Ite28099s-Okay-To-Write-Unit-Tests.aspx