【问题标题】:Should I test if a stubbed method was called?我是否应该测试是否调用了存根方法?
【发布时间】:2010-02-25 07:04:31
【问题描述】:

我刚开始使用 MSpec(使用 James Broome 的 AutoMocking)和 RhinoMocks 进行 BDD/TDD。以下是我的实践项目的摘录:

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);
    }

如您所见,我在 MovieRepository 类中删除了 FindMovies() 方法。然后我调用 MoviesController.Find() 操作。我的问题是,是否应该有一个断言来检查控制器是否调用了存根方法(FindMovies)?或者也许我应该只关心返回的结果而不关心它的来源?此外,“should_fetch_movies_from_the_repository”的规范看起来很像一个工程任务,而不是客户可能理解的东西——它在 BDD 中有它的位置吗?

【问题讨论】:

    标签: c# mocking bdd stub


    【解决方案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 方法,则其他测试将失败并通知您有问题。

    【讨论】:

    • 对,现在我看到如果我不在控制器操作中使用 FindMovies,测试将会失败。但是,如果我以与调用 FindMovies 不同的方式创建了相同的列表,则测试将通过。我想我不应该关心数据来自哪里,只要它是有效的,对吧?
    • 你在乎,你不在乎。您关心数据是从外部存储机制加载的 - 但您并不真正关心它是如何从该存储机制加载的。真正的好处是,当您更改 API 或实现时,您不会创建开始失败的脆弱测试。例如,如果您决定将 FindMovies 调用替换为具有称为“FindMoviesInCache”的方法的缓存机制,那么只要存根 FindMoviesInCache 返回正确的数据,您的测试仍然会通过。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    相关资源
    最近更新 更多