如何正确模拟一个简单的 CRUD 界面?

How do I mock a simple CRUD interface correctly?

假设我有以下简单的示例界面:

public interface UserDB {
    void addUser(User user);
    void updateUser(User user);
    User getUser(String id);
    void deleteUser(String id);
}

我想用 Mockito 编写测试来测试简单的 CRUD 操作。我想验证以下内容:

我的想法是,我需要这样的东西:

    UserDB udb = mock(UserDB.class);
    when(udb.getUser("1")).thenThrow(new UserNotFoundException());
    when(udb.addUser(new User("1"))).when(udb.getUser("1").thenReturn(new User("1"));

但是,像最后一行这样的东西不是正确的 Mockito 语法。对于不同的先决条件或调用的方法的不同顺序,我如何检查验证不同的结果?

这样做是一种代码味道。事实上,您想编写所有这些代码来查看 "user cannot be added twice" 是否真的基本上只是编写一个新的 class,这与您的数据库规则无关。

这是一个你可以做的事情的想法;将验证规则构建为数据库上的 Decorator,然后使用模拟 "undecorated" 数据库测试装饰器本身。例如:

public class ValidatingUserDB implements UserDB {
  private UserDB delegate;

  public ValidatingUserDB(UserDB delegate) {
    this.delegate = delegate;
  }

  public void addUser(User user) {
    User oldUser = delegate.get(user.getId());
    if (oldUser != null) throw new IllegalArgumentException(
             "User " + user.getId() + " already exists!";
    delegate.addUser(user);
  }
}

然后,您将像这样编写测试:

@Test(expected=IllegalArgumentException.class)
public void testNoDuplicateUsers() {
  User sampleUser = new User("1");
  UserDB delegate = mock(UserDB.class);
  when(delegate.getUser(any(User.class))).thenReturn(sampleUser);
  UserDB db = new ValidatingUserDB(delegate);
  db.addUser(sampleUser);
}

public void testAddingUser() {
  User sampleUser = new User("1");
  UserDB delegate = mock(UserDB.class);
  UserDB db = new ValidatingUserDB(delegate);
  db.addUser(sampleUser);
  verify(delegate).getUser(sampleUser);
  verify(delegate).addUser(sampleUser);
}

通过将验证行为与 CRUD 行为分开,您可以自己编写测试,而无需使用超级复杂的答案规则等重写所有这些测试。