【问题标题】:Mocking objects that encapsulate collections模拟封装集合的对象
【发布时间】:2011-10-17 12:44:05
【问题描述】:

我想知道如何检查一个方法是否返回一个容器,该容器封装了一些集合,该集合是模拟对象返回的多个其他容器的集合。也就是说,它包含各个容器的所有元素。我在其他地方进行了一些测试来检查容器“有效”(add/addAll/etc),所以我知道这有效,但我不确定如何进行“createsRoadUsersAccordingToAllAddedCreators”下面的测试。

我有一个 RoadUserCreationDaemon 类,我调用它根据添加的 RoadUserCreator 返回一个 RoadUserContainer。简化版:

public class RoadUserCreationDaemon {

    private SimulationManager simulationManager;
    private List<RoadUserCreator> roadUserCreators;

    public RoadUserCreationDaemon(SimulationManager simulationManager) {
        this.simulationManager = simulationManager;
        roadUserCreators = new ArrayList<RoadUserCreator>();
    }

    public void addRoadUserCreator(RoadUserCreator roadUserCreator) {
        roadUserCreators.add(roadUserCreator);
    }

    public RoadUserContainer createRoadUsers() {
        RoadUserContainer roadUsers = new RoadUserContainerImpl(); 
        for (RoadUserCreator creator : roadUserCreators) {
            roadUsers.addAll(createRoadUsers(creator));
        }
        return roadUsers;
    }

    public RoadUserContainer createRoadUsers(
            RoadUserCreator roadUserCreator) {
        return roadUserCreator.create();
    }
}

我首先为 createRoadUsers 编写了一个测试 (JUnit4 / JMock2.5.1),它返回一个带有提供的创建者的 RoadUserContainer。然后我开始为非参数化的 createRoadUsers 编写一个测试,看看它是否返回一个容器,其中包含创建者返回的各个容器的所有元素:

@RunWith(JMock.class)
public class TestRoadUserCreationDaemon {
    Mockery context = new JUnit4Mockery();      
    private RoadUserCreationDaemon daemon;      
    private RoadUserCreator roadUserCreator;    
    private SimulationManager simulationManager;        
    private RoadUserContainer createdRoadUsers;

    @Before
    public void setUp() {
        simulationManager = context.mock(SimulationManager.class);
        daemon = new RoadUserCreationDaemon(simulationManager);

        roadUserCreator = context.mock(RoadUserCreator.class);
        createdRoadUsers = context.mock(RoadUserContainer.class);
    }       

    @Test
    public void createsRoadUsersAccordingToAllAddedCreators() throws Exception {
        final RoadUserCreator anotherRoadUserCreator = context.mock(RoadUserCreator.class, "anotherRUC");
        final RoadUserContainer moreCreatedRoadUsers = context.mock(RoadUserContainer.class, "moreCRU");
        context.checking(new Expectations() {{
            oneOf (roadUserCreator).create(); will(returnValue(createdRoadUsers));
            oneOf (anotherRoadUserCreator).create(); will(returnValue(moreCreatedRoadUsers));

            oneOf (createdRoadUsers).roadUsersAsList();
            oneOf (moreCreatedRoadUsers).roadUsersAsList();
        }});

        daemon.addRoadUserCreator(roadUserCreator);
        daemon.addRoadUserCreator(anotherRoadUserCreator);
        daemon.createRoadUsers();

        //how to easily check that the two lists are equivilant - have same items, but not the same object?
        //assertEquals(createdRoadUsers, daemon.createRoadUsers() );
    }

    @Test
    public void createsRoadUsersAccordingToCreator() throws Exception {

        context.checking(new Expectations() {{
            oneOf (roadUserCreator).create(); will(returnValue(createdRoadUsers));
        }});
        assertEquals(createdRoadUsers, daemon.createRoadUsers(roadUserCreator));
    }
}

正如评论所说...我不确定如何以不丑陋的方式进行。

“RoadUserContainer”接口:

public interface RoadUserContainer extends Iterable<RoadUser> {
    public void add(RoadUser roadUser);
    public Iterator<RoadUser> iterator();
    public void addAll(RoadUserContainer createRoadUsers);
    public List<RoadUser> roadUsersAsList();
    public boolean equals(RoadUserContainer otherContainer);
    ...
}

我是 TDD 和 mocking 的新手,这是我 6 年来的第一个 Java 项目,所以请随意评论辅助美学!

【问题讨论】:

    标签: java testing tdd mocking encapsulation


    【解决方案1】:

    我最初可能会使用真正的容器并模拟其他对象。然后使用hamcrest 询问生成的对象。

    我想要创建的测试如下所示:

    final RoadUser roadUser0 = context.mock(RoadUser.class, "roadUser0");
    final RoadUser roadUser1 = context.mock(RoadUser.class, "roadUser1");
    final RoadUser roadUser2 = context.mock(RoadUser.class, "roadUser2");
    
    final RoadUserCreator roadUserCreator0 = context.mock(RoadUserCreator.class, "roadUserCreator0");
    final RoadUserCreator roadUserCreator1 = context.mock(RoadUserCreator.class, "roadUserCreator1");
    
    final RoadUserCreationDaemon daemon = new RoadUserCreationDaemon(null);
    daemon.addRoadUserCreator(roadUserCreator0);
    daemon.addRoadUserCreator(roadUserCreator1);        
    
    context.checking(new Expectations() {{
        oneOf(roadUserCreator0).create(); will(returnValue(roadUsers(roadUser0, roadUser1)));
        oneOf(roadUserCreator1).create(); will(returnValue(roadUsers(roadUser2)));
    }});
    
    assertThat(daemon.createRoadUsers(), contains(roadUser0, roadUser1, roadUser2));
    

    您将需要从 hamcrest 导入这些内容:

    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.Matchers.contains;
    

    如果顺序不重要,您可以使用 containsInAnyOrder 代替 contains

    您还需要创建实用方法“roadUsers”

    public static RoadUserContainer roadUsers(final RoadUser... roadUsers)
    {
        return new RoadUserContainerImpl(roadUsers);
    }
    

    另一种设计是更改 RoadUserCreationDaemon 的界面

    public void createRoadUsers(final RoadUserContainer roadUsers) {
        for (final RoadUserCreator roadUserCreator : roadUserCreators) {
            roadUsers.addAll(roadUserCreator.create());
        }
    }
    

    然后你可以这样写测试:

    final RoadUserContainer roadUserContainer0 = context.mock(RoadUserContainer.class, "roadUserContainer0");
    final RoadUserContainer roadUserContainer1 = context.mock(RoadUserContainer.class, "roadUserContainer1");
    
    final RoadUserContainer resultRoadUserContainer = context.mock(RoadUserContainer.class, "resultRoadUserContainer");
    
    final RoadUserCreator roadUserCreator0 = context.mock(RoadUserCreator.class, "roadUserCreator0");
    final RoadUserCreator roadUserCreator1 = context.mock(RoadUserCreator.class, "roadUserCreator1");
    
    final RoadUserCreationDaemon daemon = new RoadUserCreationDaemon(null);
    daemon.addRoadUserCreator(roadUserCreator0);
    daemon.addRoadUserCreator(roadUserCreator1);
    
    context.checking(new Expectations() {
        {
            oneOf(roadUserCreator0).create();
            will(returnValue(roadUserContainer0));
            oneOf(roadUserCreator1).create();
            will(returnValue(roadUserContainer1));
    
            oneOf(resultRoadUserContainer).addAll(roadUserContainer0);
            oneOf(resultRoadUserContainer).addAll(roadUserContainer1);
        }
    });
    
    daemon.createRoadUsers(resultRoadUserContainer);
    

    如果调用“addAll”的顺序很重要,您可以使用jmock sequence

    【讨论】:

      【解决方案2】:

      我想我会嘲笑造物主,但让它返回真正的容器。测试的想法是确保守护进程调用了创建者的所有创建方法,对吗?所以你的测试条件看起来像

        RoadUserContainer result = daemon.createRoadUsers();
      
        // Check that the result contains both creator's users
        Assert.assertEquals(createdRoadUsers.size() + moreCreatedRoadUsers.size(), result.size());
      
        for (RoadUser user : createdRoadUsers)
            Assert.assertTrue(result.contains(user));
        for (RoadUser user : moreCreatedRoadUsers)
            Assert.assertTrue(result.contains(user));
      

      【讨论】:

      • 好的,那么您认为可以创建实际容器的标准是容器仍然是“值”对象吗?
      • 它不必是一个值对象 - 只需验证由守护进程创建的容器是否包含由 2 个创建者对象创建的所有用户。这有意义吗?
      • 是的,我想我明白了——这就是我目前的做法(谢谢;))。然而,将它与其他对象的实际实现结合起来似乎仍然会留下一点味道。我已经对答案投了赞成票,但我会推迟一到三天将其标记为已回答。
      猜你喜欢
      • 2023-02-04
      • 1970-01-01
      • 2019-10-03
      • 1970-01-01
      • 2011-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多