Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static mocking of System and Thread #3116

Open
oliveryasuna opened this issue Sep 14, 2023 · 4 comments
Open

Static mocking of System and Thread #3116

oliveryasuna opened this issue Sep 14, 2023 · 4 comments

Comments

@oliveryasuna
Copy link

See #1013 (comment).

Will Mockito ever support static mocking of System and Thread. I have a use case where I would like to mock the return value of System#getProperty(String).

@nineninesevenfour
Copy link

nineninesevenfour commented Sep 25, 2023

Hello @oliveryasuna ,
as there are good reasons not to mock System, why does the simple approach not work for you?:

    @BeforeEach
    public void setup() {
        System.setProperty("foo", "bar");
    }

    @AfterEach
    public void tearDown() {
       System.clearProperty("foo");
    }

    @Test
    public void testSystemPropertySet() {
        assertEquals("bar", System.getProperty("foo"));
    }

Or if you have lots of them, assume you have "test.properties" on you test class path with....

foo=bar
theAnswer=42

...then you could do:

    @BeforeEach
    public void setup() throws IOException {
        // save in test class for tearDown()
        this.properties = new Properties();
        try (InputStream is = MockStaticTest.class.getResourceAsStream("/test.properties")) {
            properties.load(is);
            System.setProperties(properties);
        }
    }

    @AfterEach
    public void tearDown() {
        for (String propertyName : this.properties.stringPropertyNames()) {
            System.clearProperty(propertyName);
        }
    }

    @Test
    public void testSystemPropertySet() {
        assertEquals("bar", System.getProperty("foo"));
        assertEquals("42", System.getProperty("theAnswer"));
    }

Maybe you can give more details about your use case?

@nineninesevenfour
Copy link

nineninesevenfour commented Sep 25, 2023

I also found a call to System.setProperties(...) passing a mocked instance to work:

public class OverrideSystemPropertiesTest {

    @Test
    public void testSystemPropertySet() {
        System.setProperty("forgotten", "value will be lost");
        Properties mockedProperties = Mockito.mock(Properties.class);
        Mockito.when(mockedProperties.getProperty("foo")).thenReturn("bar");
        Mockito.when(mockedProperties.getProperty("theAnswer")).thenReturn("42");
        System.setProperties(mockedProperties);
        assertEquals("bar", System.getProperty("foo"));
        assertEquals("42", System.getProperty("theAnswer"));
        assertNull(System.getProperty("unknown"));
        assertNull(System.getProperty("forgotten"));
    }

    @Test
    public void testSystemPropertyUnSet() {
        // is the limited scope of system properties a JUnit feature?
        assertNull(System.getProperty("foo"));
        assertNull(System.getProperty("theAnswer"));
    }
}

However, note that calling System.setProperties(...) will completely replace all existing properties (as pointed out with the "forgotten" property in the example).

If you want to keep properties that existed before the test execution you can use a spy:

public class EnhanceSystemPropertiesTest {

    @Test
    public void testSystemPropertySet() {
        System.setProperty("remembered", "value will stay");
        Properties spiedProperties = Mockito.spy(System.getProperties());
        Mockito.when(spiedProperties.getProperty("foo")).thenReturn("bar");
        Mockito.when(spiedProperties.getProperty("theAnswer")).thenReturn("42");
        System.setProperties(spiedProperties);
        assertEquals("bar", System.getProperty("foo"));
        assertEquals("42", System.getProperty("theAnswer"));
        assertEquals("value will stay", System.getProperty("remembered"));
        assertNull(System.getProperty("unknown"));
    }
}

Hope that helps.

@nineninesevenfour
Copy link

@TimvdLippe Tim, do you think this is worth a FAQ entry like this?: https://github.com/nineninesevenfour/mockito/wiki/FAQ#can-i-mock-system-properties
I have an "Edit" button for the Mockito wiki, but I don't want to click withouth approval.
The corresponding patch is here: https://github.com/nineninesevenfour/mockito/blob/patches/.patches/mockito_faq_system_properties.patch

@TimvdLippe
Copy link
Contributor

That would contradict our guidance in https://github.com/nineninesevenfour/mockito/wiki/How-to-write-good-tests#dont-mock-a-type-you-dont-own and I don't think we want to change that. Therefore, I would consider mocking System not supported, same with Thread. If you want to set system properties, you need to do so in your test setup and teardown as described above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants