简体   繁体   中英

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.

My menu class has a private VerticalOptions object and a public handleInput method. 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.

I want to make a unitTest to see if the methods being called are the correct ones, how can I do that?

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.

I've also tried putting the spy on the VerticalOptions object, after getting it with a getVerticalOptions method, but it also doesn't work.

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(); -> at MenuTest.testInput(MenuTest.java:60) Actually, there were zero interactions with this mock.

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.

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.

For example sake, let's say we create a string with 3 possible values: cycleDown, cycleUp and 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.

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. 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. Later on you start looking at integration tests, Automated UI tests, but those are different things. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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