Когда я использую Moq непосредственно издеваться IBuilderFactory
и инстанцирует BuilderService
себя в модульном тесте, я могу пройти тест на прохождение который проверяет, что Create()
метод IBuilderFactory
называется ровно один раз.Почему Autofixture w/AutoMoqCustomization перестает жаловаться на отсутствие безпараметрического конструктора при закрытии класса?
Однако, когда я использую Autofixture с AutoMoqCustomization, замораживание издеваться над IBuilderFactory
и инстанцировании BuilderService
с fixture.Create<BuilderService>
, я получаю следующее исключение:
System.ArgumentException: Невозможно создать экземпляр прокси-сервер class: OddBehaviorTests.CubeBuilder. Не удалось найти конструктор без параметров . Имя параметра: constructorArguments
Если я CubeBuilder
запечатано (в лице, заменив его запечатанный класс SealedCubeBuilder
, который вызывается IBuilderFactoryForSealedBuilder.Create()
, тест проходит с использованием AutoFixture с AutoMoqCustomization, без исключения, брошенного
Am. Я пропустил что-то? Поскольку я получаю прохождение тестов с использованием Moq напрямую, я считаю, что это связано с Autofixture и/или AutoMoqCustomization.Это желаемое поведение? Если да, то почему?
Для воспроизведения я использую:
using Moq;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
using Xunit;
Вот четыре теста, иллюстрирующие поведение:
public class BuilderServiceTests {
[Fact]
public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() {
var factory = new Mock<IBuilderFactory>();
var sut = new BuilderService(factory.Object);
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() {
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var factory = fixture.Freeze<Mock<IBuilderFactory>>();
var sut = fixture.Create<BuilderService>();
sut.Create(); // EXCEPTION THROWN!!
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() {
var factory = new Mock<IBuilderFactoryForSealedBuilder>();
var sut = new BuilderServiceForSealedBuilder(factory.Object);
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() {
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var factory = fixture.Freeze<Mock<IBuilderFactoryForSealedBuilder>>();
var sut = fixture.Create<BuilderServiceForSealedBuilder>();
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
}
Вот необходимые классы:
public interface IBuilderService { IBuilder Create(); }
public class BuilderService : IBuilderService {
private readonly IBuilderFactory _factory;
public BuilderService(IBuilderFactory factory) { _factory = factory; }
public IBuilder Create() { return _factory.Create(); }
}
public class BuilderServiceForSealedBuilder : IBuilderService {
private readonly IBuilderFactoryForSealedBuilder _factory;
public BuilderServiceForSealedBuilder(IBuilderFactoryForSealedBuilder factory) { _factory = factory; }
public IBuilder Create() { return _factory.Create(); }
}
public interface IBuilderFactoryForSealedBuilder { SealedCubeBuilder Create(); }
public interface IBuilderFactory { CubeBuilder Create(); }
public interface IBuilder { void Build(); }
public abstract class Builder : IBuilder {
public void Build() { } // build stuff
}
public class CubeBuilder : Builder {
private Cube _cube;
public CubeBuilder(Cube cube) { _cube = cube; }
}
public sealed class SealedCubeBuilder : Builder {
private Cube _cube;
public SealedCubeBuilder(Cube cube) { _cube = cube; }
}
public class Cube { }
Спасибо за расшифровку этого! Смешанные эмоции, когда ответ оказывается ошибкой в библиотеке, которую я использую ... Я случайно наткнулся на это, потому что некоторые из моих классов XXXBuilder были запечатаны, а некоторые нет. Обнаружен аргумент о том, следует ли запечатывать классы в отсутствие веской причины не запечатывать или делать наоборот. Похоже, что классы уплотнения могут вызвать трение на модульные испытания - позаботиться о том, чтобы поделиться мнением по этому вопросу? – Jeff
Понятие о том, что закрытые классы боятся тестируемости, возникает (я думаю) из Java, где все члены виртуальны по умолчанию. В C# это не так, поэтому вопрос становится менее важным. В любом случае, если вы предпочитаете композицию над наследованием, это вряд ли имеет значение. FWIW, F #, такие как записи, компилируются в запечатанные классы, но код F # может быть очень подвержен тестированию. В конечном счете, если вы будете следовать TDD, у вас будет тестовый код :) –
Я должен уточнить, что закрытые классы, по-видимому, вредят тестируемости, когда издевательская структура в использовании не может издеваться над закрытыми классами, но ее просят сделать это. Затем остается выбирать между реализацией определенной функции доступности, [как заявляют известные люди] (http://stackoverflow.com/a/2165287/533958), делая класс незапечатанным просто потому, что * mocking tool * нуждается в этом и сохраняет класс запечатанным, поэтому не нужно разрабатывать для наследования. Тем не менее, в отношении разработки TDD. – Jeff