Имитация вызова конструктора в Justmock не удалась для UrlHelper

В justmock мы можем возвращать фиктивные экземпляры вместо реальных экземпляров, упорядочив вызов конструктора, например

Mock.Arragne(()=>new MyClass(Arg.IsAny<string>())).IgnoreInstance().Returns(Mock.Create<MyClass>());

но когда я попробовал то же самое с классом UrlHelper, вместо издевательского типа создается экземпляр фактического типа. Может ли кто-нибудь сказать, есть ли в этом ошибка:

Класс УрлМодел

    public class UrlModel
    {
        private UrlHelper url;
        public UrlModel()
        {
           url = new UrlHelper(HttpContext.Current.Request.RequestContext);
        }
    }

Метод испытания :

public void UrlTest()
{
   Mock.Arrange(() => HttpContext.Current.Request.RequestContext).Returns(Mock.Create<RequestContext>());

    var mockedUrl = Mock.Create<UrlHelper>();

    Mock.Arrange(() => new UrlHelper(Arg.IsAny<RequestContext>()))
        .IgnoreArguments()
        .IgnoreInstance()
        .Returns(mockedUrl);

    //Here url will have actual instance instead of mocked instance
    var model = new UrlModel();

    //Assert is ommitted for bravity .. 
}

person Necromancer    schedule 30.09.2016    source источник


Ответы (3)


Вы можете использовать Typemock для тестирования своего кода без добавления каких-либо новых интерфейсов, подделывая RequestContext и изменяя поведение свойств:

[TestMethod,Isolated]
public void UrlTest()
{
    //Arrange 
    var fakeRequest = Isolate.Fake.Instance<RequestContext>();
    Isolate.WhenCalled(() => HttpContext.Current.Request.RequestContext).WillReturn(fakeRequest);

    //Act
    var res = new UrlModel();
    //getting the private field so it can be asserted
    var privateField = Isolate.NonPublic.InstanceField(res, "url").Value as UrlHelper;

    //Assert
    Assert.AreEqual(fakeRequest, privateField.RequestContext);
}
person JamesR    schedule 05.10.2016

Вы вручную создаете экземпляр UrlHelper в конструкторе UrlModel. UrlModel теперь тесно связан с UrlHelper (новое — это клей). Сделайте абстракцию зависимостей, которую вы можете использовать для более слабо связанной модели и улучшенной возможности имитации.

public interface IUrlHelperAccessor {
    UrlHelper UrlHelper { get; }
}

и введите это в UrlModel

public class UrlModel {
    private UrlHelper url;
    public UrlModel(IUrlHelperAccessor accessor) {
       url = accessor.UrlHelper;
    }
    //...other code
}

Теперь вы организуете тест соответственно

public void UrlTest() {
    Mock.Arrange(() => HttpContext.Current.Request.RequestContext)
        .Returns(Mock.Create<RequestContext>());

    var mockedUrl = Mock.Create<UrlHelper>(Constructor.Mock);
    var mockedAccessor = Mock.Create<IUrlHelperAccessor>();

    Mock.Arrange(() => mockedAccessor.UrlHelper).Returns(mockedUrl);

    //Here url will have actual instance instead of mocked instance
    var model = new UrlModel(mockedAccessor);

    //Assert is omitted for brevity .. 

}
person Nkosi    schedule 30.09.2016

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

Еще одна вещь, которая приходит на ум, это то, что отладчик мог ввести вас в заблуждение. Когда вы создаете макет неабстрактного типа при работающем профилировщике, сам экземпляр имеет тот же тип, что и макетируемый тип, — это не производный тип, скажем, UrlHelperMock, как в случае, когда профилировщик не запущен. Можете ли вы подтвердить с помощью функции отладчика Make Object ID, что фиктивный экземпляр и экземпляр, возвращенный из new, действительно не совпадают?

Вы пришли к выводу, что насмешка над выражением new не работает, потому что ваши аранжировки UrlHelper не работают, или это было что-то еще?

person Stefan Dragnev    schedule 20.01.2017