我如何测试 class 方法是否从 classes 的私有对象之一调用另一个方法

How can I test if a class method is calling another method from one of the classes's private objects

我正在尝试为项目创建单元测试。在我的项目中,我有一个菜单 Class 和一个 VerticalOptions Class.

我的菜单 class 有一个私有的 VerticalOptions 对象和一个 public handleInput 方法。 当我调用我的菜单的 handleInput(key) 方法时,根据我给它的键,它会做不同的事情,即调用我的 VerticalOptions 对象的不同方法。

我想做一个 unitTest 看看被调用的方法是否正确,我该怎么做?

我已经尝试在我的菜单中添加一个 Mockito 间谍,但是因为我想测试被调用的方法是否是私有 VerticalOptions 对象中的方法,所以它实际上不起作用。

在使用 getVerticalOptions 方法获取 VerticalOptions 对象后,我也尝试过将它放在 VerticalOptions 对象上,但它也不起作用。

public void handleInput(InputKey key)
{
    switch (key) {
        case S:
        case DOWN:
            optionsInterface.cycleDown();
            break;
        case W:
        case UP:
            optionsInterface.cycleUp();
            break;
        case SPACE:
        case ENTER:
            optionsInterface.select();
            break;
        default:
            break;
    }
}


@Test
public void testInput() {
    MainMenu menu = new MainMenu(game);
    VerticalButtonInterface buttonInterface = menu.getOptionsInterface();
    VerticalButtonInterface spy = spy(buttonInterface);

    menu.handleInput(InputKey.DOWN);
    verify(spy, times(1)).cycleDown();
}

这是我得到的测试失败:

需要但未调用: verticalButtonInterface.cycleDown(); -> 在 MenuTest.testInput(MenuTest.java:60) 实际上,与此模拟的交互为零。

我给你换个说法。我见过很多人走错了路,当你这样做时,其他一切都变得很难做/测试,这正是你现在正在做的。

从这里开始,你想达到什么目的?

I want to test and make sure that a certain method is called ...

这是好事吗?什么是不应该有的单元测试?那就是对代码的深入了解。

为什么?因为每次你对代码做一点小改动,你都会因为这种深厚的知识而不得不改变测试。如果您进行了 1000 次测试,那么您的道路就很艰难。

好的,我们现在知道问题是什么了,那么我们如何解决呢?好吧,首先让我们确保我们可以在不深入了解代码的情况下进行测试。

我们该怎么做?好吧,想象一下你的代码添加了一个额外的步骤,一个设置状态的标志。您可能有一个存储结果状态的标志 ...

你有 3 个方法要调用,所以你需要 3 个不同的状态,所以创建一个变量来反映它,无论是字符串、枚举还是其他任何让你满意的东西。

例如,假设我们创建了一个包含 3 个可能值的字符串:cycleDown、cycleUp 和 select。

您的代码开始看起来像:

public string handleInput(InputKey key)
{
      String state = determineState(key);
      SomeType someResult = executeActionForState(state);
}

public String determineState(string key)
{

    String state = "";
     switch (key) {
    case S:
    case DOWN:
        state = "cycleDown";
        break;
    case W:
    case UP:
        state = "cycleUp";
        break;
    case SPACE:
    case ENTER:
        state = "select";
        break;
    default:
        break;
}

return state;
}

public void executeActionForState(string state)
{
     if ( state == "cycleup" ) {
     }

     etc etc
}

现在,我可能不一定像这样编写您的示例代码,这有点勉强,这取决于您对代码执行的其他操作,但这是为了展示如何将功能与 UI 方面。

我可以很容易地测试状态方法,我可以更改它的代码而不必更改测试,因为测试会查看输入和输出,而不是事情是如何实现的。

单元测试是关于功能的,它是关于拥有一旦创建就不需要更改的简单测试。验证某个方法是否已被调用不会给您带来任何有价值的东西,因为您不知道该方法稍后会做什么。

UI 你可以用其他方式测试的东西,单元测试只是关于正确的功能。如果你不明确这种分离,那么你将很难维护你的测试,它会变得越来越难,直到你放弃。

您将测试您是否获得了正确的状态,然后测试 cycleUp 方法是否根据您的要求做了正确的事情,这就是您知道每个部分独立工作的方式。稍后您开始查看集成测试、自动化 UI 测试,但这些是不同的东西。对它的目的进行单元测试,保持简单,不与其他代码绑定,然后一切都会变得简单。你不需要模拟太多,你不需要太担心复杂的设置,你也不需要每次代码中的某些内容发生变化时都改变你的测试。

现在,为了解决问题的最后一部分,私有方法,您通过观察它们的输出来测试它们。您的 class 中必须有一些东西 public 在调用私有方法时发生变化。所以测试一下。