Когда я высмеиваю свой контроллер ASP.NET MVC, мой ActionMethod не возвращает вид. Зачем? - PullRequest
2 голосов
/ 24 октября 2011

В моем простом Index() ActionMethod я ссылаюсь на свойство User.Identity. Итак, я подумал, что мне нужно это высмеять.

Итак, я создаю макет HomeController и использую его в своем модульном тесте. Когда я это делаю, ActionMethod возвращает ноль в качестве представления. Когда я заменяю контроллер mock'd конкретным экземпляром (и, конечно, закомментирую любую ссылку на User.Identity), тогда возвращается правильное представление.

например.

// Arrange.
var homeController = Mock<HomeController>(..);
homeController.Setup(x => x.User).Returns(new GenericPrincipal(..));

// Act.
var result = homeController.Index();

// Assert.
Assert.IsNotNull(result); // This fails here. result is NULL.

но когда я делаю это (и закомментирую любую ссылку User), это работает ...

// Arrange.
var homeController = new HomeController(..);

// Act.
var result = homeController.Index();

// Assert.
Assert.IsNotNull(result); // Tick!

Есть идеи, почему это так?

Ответы [ 4 ]

3 голосов
/ 24 октября 2011

Есть некоторые странные вещи в вашем модульном тесте. Вы проводите модульное тестирование контроллера, и тем не менее вы издеваетесь над созданием тестируемого объекта: var homeController = Mock<HomeController>(..);, что неверно.

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

// arrange
var sut = new HomeController();
var user = new GenericPrincipal(new GenericIdentity("foo"), null);
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(x => x.User).Returns(user);
var context = new ControllerContext(new RequestContext(httpContext.Object, new RouteData()), sut);
sut.ControllerContext = context;

// act
var actual = sut.Index();

// assert
...
2 голосов
/ 24 октября 2011

Я думаю, что вы должны насмехаться над HttpContext, чтобы контроллер работал. Я предоставил один ответ на другой, который вы могли бы использовать .Как говорит Стив Роботхэм , вы должны высмеивать зависимости тестируемой системы (т.е. зависимости контроллера), а не издеваться над самой тестируемой системой;вы хотите протестировать реальную версию контроллера, а не макет :))

Используя класс HttpContextBase по ссылке, вы просто сделаете следующее в своем тесте

var controllerToTest = new HomeController();
var context = new MockHttpContextBase(controllerToTest);

// do stuff that you want to test e.g. something goes into session

Assert.IsTrue(context.HttpContext.Session.Count > 0); 

Youможет пойти еще дальше и создать методы Setup и TearDown для настройки контроллера с поддельным контекстом.

1 голос
/ 24 октября 2011

Я думаю, что ваш метод Index, вероятно, виртуальный, что заставляет Moq заменить эту функцию на фиктивную функцию. Чтобы предотвратить это, вам нужно установить свойство CallBase на макете.

Однако я согласен с другими ответами, что вы не должны издеваться над своим контроллером, но вы должны высмеивать свою зависимость.

(еще более простой метод - создать специализированный механизм связывания моделей, который может извлекать принципал из HttpContext, тогда вы можете просто передать принципал в качестве входного параметра в ваш метод)

1 голос
/ 24 октября 2011

Если честно, это выглядит как очень странный тест, потому что вы издеваетесь над тестируемой системой (SUT), другими словами HomeController.Обычно можно смоделировать зависимости SUT, установить ожидания для макета и вставить макет в SUT, чтобы убедиться, что он правильно взаимодействует со своими зависимостями.

Когда вы создаете макет HomeControllerMoq создает класс, который наследуется от HomeController и переопределяет виртуальный метод Index.Поэтому, когда вы вызываете Index на макете, вы вызываете не реализацию Index, которую вы определили в классе HomeController, а переопределенный.Поскольку вы не указали явно Setup метод в макете, он вернет значение по умолчанию, в этом случае null.

Во втором тесте вы вызываете фактическую реализацию Index, потому чтовы создаете фактический экземпляр класса HomeController.Если вы вызовете GetType() для экземпляра фиктивного объекта, то увидите, что это экземпляр прокси-сервера, производного от HomeController, который перехватывает вызовы общедоступных переопределяемых методов базового класса (который является большимчасть цели макета объекта).

...