简体   繁体   English

如何测试类方法是否正在从类的私有对象之一调用另一个方法

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

I'm trying to create Unit tests for a project. 我正在尝试为一个项目创建单元测试。 In my project I have a Menu Class, and a VerticalOptions Class. 在我的项目中,我有一个菜单类和一个VerticalOptions类。

My menu class has a private VerticalOptions object and a public handleInput method. 我的菜单类有一个私有的VerticalOptions对象和一个公共的handleInput方法。 When I call my menu's handleInput(key) method, depending on the key I give it it'll do different things, namely call different methods of my VerticalOptions object. 当我调用菜单的handleInput(key)方法时,根据我提供的键,它会执行不同的操作,即调用VerticalOptions对象的不同方法。

I want to make a unitTest to see if the methods being called are the correct ones, how can I do that? 我想做一个unitTest来查看被调用的方法是否正确,我该怎么做?

I've tried adding a Mockito spy to my menu, however since I want to test if the method being called was the method in the private VerticalOptions object, it doesn't really work. 我尝试将Mockito间谍添加到菜单中,但是由于我想测试被调用的方法是否是私有VerticalOptions对象中的方法,因此它实际上不起作用。

I've also tried putting the spy on the VerticalOptions object, after getting it with a getVerticalOptions method, but it also doesn't work. 在使用getVerticalOptions方法获取间谍程序之后,我还尝试了将间谍放在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();
}

This is the test failure I got: 这是我的测试失败:

Wanted but not invoked: verticalButtonInterface.cycleDown(); 想要但不被调用:verticalButtonInterface.cycleDown(); -> at MenuTest.testInput(MenuTest.java:60) Actually, there were zero interactions with this mock. ->在MenuTest.testInput(MenuTest.java:60)上,实际上与该模拟的交互为零。

I will give you an alternative view on this. 我将为您提供另一种观点。 I have seen a lot of people going down the wrong path and when you do that, everything else becomes hard to do / test, which is exactly what you are doing now. 我已经看到很多人走错了道路,当您这样做时,其他所有事情都会变得难以执行/测试,这正是您现在正在做的事情。

Start here, what are you trying to achieve? 从这里开始,您要达到什么目标?

I want to test and make sure that a certain method is called ... 我想测试并确保某种方法被称为...

Is this a good thing? 这是一件好事吗? What is a unit test not meant to have? 什么不是单元测试? That is deep knowledge of the code. 那就是对代码的深入了解。

Why? 为什么? Because every time you make slight changes to the code, you'll have to change the test because of this deep knowledge. 因为每次您对代码进行微小的更改时,由于这些深厚的知识,您都必须更改测试。 if you have 1000 tests, you're in for a hard road. 如果您有1000项测试,那您将步履艰难。

Ok, so we now know what the problem is, so how do we solve it? 好的,现在我们知道了问题所在,那么如何解决呢? Well, first let's make sure we can have a test without deep knowledge of the code. 好吧,首先让我们确保无需进行深入的代码测试就可以进行测试。

How do we do that? 我们该怎么做? Well, imagine that your code adds an extra step, a flag which sets a state. 好吧,假设您的代码添加了一个额外的步骤,即设置状态的标志。 You might have a flag which stores a resulting state ... 您可能有一个存储结果状态的标志...

you have 3 methods you want to call, so you will need 3 different states, so create a variable which reflects that, be it a string, or an enum or whatever else makes you happy. 您有3个要调用的方法,所以您将需要3个不同的状态,因此请创建一个反映该变量的变量,它可以是字符串,枚举或其他使您满意的变量。

For example sake, let's say we create a string with 3 possible values: cycleDown, cycleUp and select. 例如,假设我们创建一个具有3个可能值的字符串:cycleDown,cycleUp和select。

your code starts to look something like : 您的代码开始看起来像:

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
}

Now, I may not necessarily code your example like this, this is a bit forced, it depends on what other things you're doing with the code, but this is meant to show how you separate functionality from UI aspects. 现在,我不一定要像这样编写您的示例,这有点强迫,它取决于您对代码执行的其他操作,但这只是为了说明如何将功能与UI方面分开。

I can easily test the state method, I can change its code and I wouldn't have to change the test, because the test would look at the inputs and outputs and not how things are achieved. 我可以轻松地测试状态方法,可以更改其代码,而不必更改测试,因为该测试将查看输入和输出,而不是如何实现。

Unit testing is about functionality, it's about having simple tests that don't need to change once created. 单元测试是关于功能的,它涉及的是简单的测试,一旦创建就不需要进行更改。 Verifying that a method has been called doesn't give you anything worthwhile, because you don't know what that method does later. 验证方法已被调用不会给您带来任何价值,因为您不知道该方法以后会做什么。

UI stuff you can test in other ways, unit testing is only about correct functionality. 您可以通过其他方式测试UI内容,而单元测试仅与正确的功能有关。 If you do not make this separation clear then you will have trouble maintaining your tests, it will become harder and harder until you give up. 如果您没有明确区分,那么您将很难维护测试,在您放弃之前,它将越来越难。

You would test that you get the correct state, then you test that cycleUp method does something correct based on your requirements and that's how you know each part works in isolation. 您将测试您是否获得了正确的状态,然后测试了cycleUp方法根据您的要求执行了正确的操作,这就是您知道每个部分独立工作的方式。 Later on you start looking at integration tests, Automated UI tests, but those are different things. 稍后,您开始研究集成测试,自动UI测试,但这是不同的。 Keep unit testing for what it's meant to do, keep it simple, keep it not tied to other code and then everything becomes simple. 保持单元测试的目的,保持简单,使其不与其他代码绑定,然后一切都变得简单。 You won't need to mock much, you won't need to worry too much about complex setups and you won't need to change your tests every time something in the code changes. 您无需进行太多模拟,也不必为复杂的设置担心太多,也不必每次代码更改时都更改测试。

Now, to address the final part of the question, private methods, you test them by observing their outputs. 现在,要解决问题的最后一部分,私有方法,您可以通过观察它们的输出来对其进行测试。 You must have something public in your class that changes when a private method is called. 您必须在类中拥有一些公共的东西,这些东西在调用私有方法时会发生变化。 So test that. 所以测试一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM