Должен ли я проверить, был ли вызван заглушенный метод?

Я только начинаю работать с BDD/TDD, используя MSpec (с AutoMocking Джеймсом Брумом) и RhinoMocks. Вот отрывок из моего практического проекта:

namespace Tests.VideoStore.Controllers
{
    public abstract class context_for_movie_controller :
    Specification<MovieController>
    {
        private static IList<Movie> movies;
        protected static IMovieRepository _movieRepository;
        protected static ActionResult _result;
        protected static string title;
        protected static string director;

        Establish context = () =>
        {
            _movieRepository = DependencyOf<IMovieRepository>();
        };
    }

    [Subject(typeof(MovieController))]
    public class when_searching_for_movies_with_director :
    context_for_movie_controller
    {
        Establish context = () =>
        {
            title = null;
            director = "James Cameron";

            var movie4 = new Movie {
                Title = "Terminator", Director = "James Cameron"};
            var movie6 = new Movie {
                Title = "Avatar", Director = "James Cameron"};

            movies = new List<Movie> {movie4, movie6};

            // Repository returns all movies.
            _movieRepository.Stub(x => x.FindMovies(title, director))
            .Return(movies);
        };

        Because of = () => _result = subject.Find(title, director);

        It should_fetch_movies_from_the_repository = () =>
            _movieRepository.AssertWasCalled(x =>
                x.FindMovies(title, director));

        It should_return_a_list_of_movies_matching_the_director = () =>
            _result.ShouldBeAView().And()
            .ShouldHaveModelOfType<IEnumerable<Movie>>)
            .And().ShouldContainOnly(movies);
    }

Как видите, я заглушил метод FindMovies() в классе MovieRepository. Затем я вызываю действие MoviesController.Find(). Мой вопрос: должно ли быть утверждение, чтобы проверить, был ли вызван контроллером заглушенный метод (FindMovies)? Или, возможно, мне следует заботиться только о возвращаемом результате, а не о том, откуда он был взят? Более того, спецификация, в которой говорится, что «should_fetch_movies_from_the_repository» очень похожа на инженерную задачу, а не на то, что клиент может понять — есть ли у нее место в BDD?


person Pawel Krakowiak    schedule 25.02.2010    source источник


Ответы (1)


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

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

поэтому вам не нужно утверждать вызовы метода FindMovies.

в противовес этому, если у вас есть макет или заглушка, которая является чисто выходным - скажем, интерфейс IView, вызываемый классом Presenter, тогда вы хотите утверждать против вызываемого IView. например, этот код:


public class MyPresenter
{
  ... other code here

  public DoSomething()
  {
    IList data = GetSomeData();
    myView.DisplayData(data);

  }
}

вы хотели бы утверждать, что в этом случае вызывается метод view.DisplayData, потому что вы не получаете ничего из этого вызова, что может быть подтверждено другим тестом.

что касается «выборки из репозитория» - конечно, ваших клиентов это волнует. они хотят, чтобы система сохраняла фильмы в хранилище и загружала их из хранилища. однако ... вызов FindMovies является входом в тестируемый класс, поэтому вообще нет необходимости иметь этот актив или тест. если метод FindMovies не вызывается, то другой тест завершится ошибкой и сообщит вам о наличии проблемы.

person Derick Bailey    schedule 25.02.2010
comment
Хорошо, теперь я вижу, что тест провалился бы, если бы я не использовал FindMovies в действии контроллера. Однако, если бы я каким-то образом создал тот же список другими средствами, а не вызовом FindMovies, тест прошел бы. Я думаю, мне не следует заботиться о том, откуда берутся данные, если они действительны, верно? - person Pawel Krakowiak; 26.02.2010
comment
ты делаешь и тебе все равно. вам важно, чтобы данные были загружены из внешнего механизма хранения, но вам все равно, как они были загружены из этого механизма хранения. реальная выгода заключается в том, что вы не создаете хрупкие тесты, которые начинают давать сбой при изменении вашего API или реализации. если вы решите заменить свой вызов FindMovies механизмом кэширования, который, например, имеет метод FindMoviesInCache, ваш тест все равно пройдет, пока заглушенный FindMoviesInCache возвращает правильные данные. - person Derick Bailey; 26.02.2010