Java 当测试一个方法时,你能在该方法所引用的单例上模拟变量吗?

Java 当测试一个方法时,你能在该方法所引用的单例上模拟变量吗?,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,我想用JUnit/Mockito测试一个方法 该方法接受一个整数,用户想要的行数 然后,该方法将该值与另一个值totalNumberOfRowsInDB进行比较,并返回请求的行数(随机选取)。如果用户请求的数量超过可用数量,他们只会得到可用的总数 到目前为止一切都很好 我的问题是,测试这种方法的行为 totalNumberOfRowsInDB是一个保存在单例上的变量。我这样做的原因是因为它将在启动时设置,并且在应用程序执行期间不会更改,并且我希望避免对DB不断的unsecssay调用,以便在它永

我想用JUnit/Mockito测试一个方法

该方法接受一个整数,
用户想要的行数

然后,该方法将该值与另一个值
totalNumberOfRowsInDB
进行比较,并返回请求的行数(随机选取)。如果用户请求的数量超过可用数量,他们只会得到可用的总数

到目前为止一切都很好

我的问题是,测试这种方法的行为

totalNumberOfRowsInDB
是一个保存在单例上的变量。我这样做的原因是因为它将在启动时设置,并且在应用程序执行期间不会更改,并且我希望避免对DB不断的unsecssay调用,以便在它永远不会更改的情况下不断计算行数

如果我让我的测试假设总共有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;
    }
}

使用反射,可以修改专用字段:

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

使用反射,可以修改专用字段:

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
我这样做的原因是因为它将在启动时设置,并且在应用程序执行期间不会更改

在这种情况下,我建议为这个属性公开setter,这样您的类肯定更“可测试”。在我看来,为这样的目的编写singleton不是一个好主意

无论如何,为了修改类中用于单元测试的私有字段(例如在生产代码中自动连接),您可以使用例如
org.springframework.test.util.ReflectionTestUtils
(或者根据它们的代码编写您自己的反射解决方案)

我这样做的原因是因为它将在启动时设置,并且在应用程序执行期间不会更改

在这种情况下,我建议为这个属性公开setter,这样您的类肯定更“可测试”。在我看来,为这样的目的编写singleton不是一个好主意


无论如何,为了修改类中用于单元测试的私有字段(例如,在生产代码中自动连接),您可以使用例如
org.springframework.test.util.ReflectionTestUtils
(或者根据它们的代码编写您自己的反射解决方案)。

您的意思是让测试依赖于“真实”数据吗?i、 表中的实际行数??现在还不清楚您到底想要“模拟”什么,但是如果您想要真实的、非擦洗的数据,我认为您将跳出“纯单元测试”的界限-这取决于您来自哪个阵营-如果要在与“外部世界”没有任何连接的情况下单独测试被测单元,那么您只需“模拟”但是如果你真的希望它基于实际的计数,那么你将处于集成测试的领域(根据pursists的POV),你的意思是让测试依赖于“真实”的数据吗?i、 表中的实际行数??现在还不清楚您到底想要“模拟”什么,但是如果您想要真实的、非擦洗的数据,我认为您将跳出“纯单元测试”的界限-这取决于您来自哪个阵营-如果要在与“外部世界”没有任何连接的情况下单独测试被测单元,那么您只需“模拟”但是如果您确实希望它基于实际计数,那么您将处于集成测试领域(根据pursists的POV)