При тестировании метода, можете ли вы смоделировать переменные на синглетах, на которые ссылается этот метод? - PullRequest
0 голосов
/ 06 августа 2011

У меня есть метод, который я хотел бы проверить с помощью JUnit / Mockito.

Метод принимает одно целое число, numberOfRowsThatTheUserWants.

Затем метод сравнивает это значение с другим значением totalNumberOfRowsInDB и возвращает запрашиваемое количество строк (выбирается случайным образом). Если пользователь запрашивает больше, чем доступно, он просто получит общее количество, которое доступно.

Все хорошо до этого момента.

Проблема, с которой я столкнулся, это проверка поведения этих методов.

totalNumberOfRowsInDB - переменная, хранящаяся в синглтоне. Моя причина была в том, что он был установлен при запуске и не менялся во время выполнения приложения, и я хотел избежать постоянных необоснованных обращений к БД для постоянного подсчета строк, когда оно никогда не изменится.

Тестирование работает, если я заставляю свои тесты предполагать, что в общей сложности 10 строк (поскольку это то, что у меня есть на фактической базе), однако я не хочу, чтобы мои тесты зависели от моих данных очистки, потому что если я добавьте еще несколько строк, это сломает тесты.

Вопрос: Как я могу изменить (смоделировать) статическую переменную, которая находится ВНУТРИ метода, который я на самом деле пытаюсь проверить? Я полностью открыт для предложений / советов о том, как улучшить мой дизайн если это ужасно неправильно ...

Это небольшая демонстрация, демонстрирующая упрощенную версию моей проблемы:

public class MockMe
{
    public static void main(String[] args)
    {
        // Setting to 5, but this comes from user input
        Integer numberOfRowsThatTheUserWants = 5;
        MockMe myMockMe = new MockMe();
        myMockMe.myMethod(numberOfRowsThatTheUserWants);
    }

    private void myMethod(Integer numberOfRowsThatTheUserWants)
    {
        if(numberOfRowsThatTheUserWants > MySingleton.getInstance().getTotalRowsOnDB())
        {
            System.out.println("You've requested more than is available, so I'll just give you all I've got instead");
        }
        else
        {
            System.out.println("Here are your " + numberOfRowsThatTheUserWants + " rows as requested, sir!");
        }
    }
}

class MySingleton
{
    private static MySingleton mySingleton;
    private int totalRowsOnDB;

    private MySingleton()
    {
        //private constructor to prevent instantiation
    }

    public int getTotalRowsOnDB()
    {
        return totalRowsOnDB;
    }

    public static MySingleton getInstance()
    {
        if (mySingleton == null)
        {
            mySingleton = new MySingleton();
            // this is actually set by SELECT COUNT(*) FROM TABLENAME but setting to 10 for purposes of demo
            //DBManager dbManager = new DBManagerImpl();
            //mySingleton.totalRowsOnDB = dbManager.getTotalRows();
            mySingleton.totalRowsOnDB = 10;
        }
        return mySingleton;
    }
}

Ответы [ 2 ]

2 голосов
/ 06 августа 2011

Моя причина для этого состояла в том, что это было бы установлено при запуске и не изменилось бы во время выполнения приложения

В таком случае, я бы рекомендовал выставить установщик дляэто свойство, тогда ваш класс будет определенно более "тестируемым".Написание синглтона для этой цели, на мой взгляд, не очень хорошая идея.

В любом случае, для изменения закрытых полей в классе для модульных тестов (например, с автопроводкой в ​​рабочем коде) вы можете использовать, например, org.springframework.test.util.ReflectionTestUtils (или написать собственное решение для отражения на основе их кода).

2 голосов
/ 06 августа 2011

Используя отражение, вы можете изменить приватное поле:

Field field = MySingleton.class.getDeclaredField("totalRowsOnDB");
field.setAccessible(true); // "Cheat" by setting it to "not private"
field.set(MySingleton.class, 5); // Set it to whatever you like
...