From ed8a28962675b540389fc9280ab1122613230fa8 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 25 Jan 2024 01:26:05 +0100 Subject: fix ProviderManagerTest, adapt InputStreamHelper and Filehelper to be mockable by injection --- .../leap/bitmaskclient/base/utils/FileHelper.java | 41 ++++++++++-- .../base/utils/InputStreamHelper.java | 24 ++++++- .../providersetup/ProviderManagerTest.java | 52 ++++++--------- .../leap/bitmaskclient/testutils/MockHelper.java | 74 +++++++++------------- 4 files changed, 110 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java index eb1c255c..f1d86876 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java @@ -2,6 +2,8 @@ package se.leap.bitmaskclient.base.utils; import android.content.Context; +import androidx.annotation.VisibleForTesting; + import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; @@ -9,19 +11,50 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import de.blinkt.openvpn.core.NativeUtils; + /** * Created by cyberta on 18.03.18. */ public class FileHelper { + + public interface FileHelperInterface { + File createFile(File dir, String fileName); + void persistFile(File file, String content) throws IOException; + } + + public static class DefaultFileHelper implements FileHelperInterface { + @Override + public File createFile(File dir, String fileName) { + return new File(dir, fileName); + } + + @Override + public void persistFile(File file, String content) throws IOException { + FileWriter writer = new FileWriter(file); + writer.write(content); + writer.close(); + } + } + + private static FileHelperInterface instance = new DefaultFileHelper(); + + @VisibleForTesting + public FileHelper(FileHelperInterface helperInterface) { + if (!NativeUtils.isUnitTest()) { + throw new IllegalStateException("FileHelper injected with FileHelperInterface outside of an unit test"); + } + + instance = helperInterface; + } + public static File createFile(File dir, String fileName) { - return new File(dir, fileName); + return instance.createFile(dir, fileName); } public static void persistFile(File file, String content) throws IOException { - FileWriter writer = new FileWriter(file); - writer.write(content); - writer.close(); + instance.persistFile(file, content); } public static String readPublicKey(Context context) { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java index 8e6273a7..6dfe0861 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java @@ -8,14 +8,36 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import de.blinkt.openvpn.core.NativeUtils; + /** * Created by cyberta on 18.03.18. */ public class InputStreamHelper { + public interface InputStreamHelperInterface { + InputStream getInputStreamFrom(String filePath) throws FileNotFoundException; + + } + + private static InputStreamHelperInterface instance = new DefaultInputStreamHelper(); + + private static class DefaultInputStreamHelper implements InputStreamHelperInterface { + @Override + public InputStream getInputStreamFrom(String filePath) throws FileNotFoundException { + return new FileInputStream(filePath); + } + } + + public InputStreamHelper(InputStreamHelperInterface helperInterface) { + if (!NativeUtils.isUnitTest()) { + throw new IllegalStateException("InputStreamHelper injected with InputStreamHelperInterface outside of an unit test"); + } + instance = helperInterface; + } //allows us to mock FileInputStream public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException { - return new FileInputStream(filePath); + return instance.getInputStreamFrom(filePath); } public static String loadInputStreamAsString(InputStream is) { diff --git a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java index e7295452..7584fb3f 100644 --- a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java @@ -1,56 +1,49 @@ package se.leap.bitmaskclient.providersetup; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static se.leap.bitmaskclient.testutils.MockHelper.mockInputStreamHelper; + import android.content.res.AssetManager; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.base.utils.ConfigHelper; import se.leap.bitmaskclient.base.utils.FileHelper; import se.leap.bitmaskclient.base.utils.InputStreamHelper; -import se.leap.bitmaskclient.providersetup.ProviderManager; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static se.leap.bitmaskclient.testutils.MockHelper.mockFileHelper; -import static se.leap.bitmaskclient.testutils.MockHelper.mockInputStreamHelper; +import se.leap.bitmaskclient.testutils.MockHelper; /** * Created by cyberta on 20.02.18. */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({ConfigHelper.class, FileHelper.class, InputStreamHelper.class}) public class ProviderManagerTest { - @Mock private AssetManager assetManager; - @Mock private File file; private ProviderManager providerManager; + InputStreamHelper inputStreamHelper; + FileHelper fileHelper; + MockHelper.MockFileHelper mockedFileHelperInterface; @Before public void setup() throws Exception { - //mock assetManager methods - //-------------------------- + assetManager = mock(AssetManager.class); + file = mock(File.class); + when(assetManager.open(anyString())).thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock invocation) throws Throwable { @@ -85,11 +78,9 @@ public class ProviderManagerTest { when(file.getAbsolutePath()).thenReturn("externalDir"); when(file.getPath()).thenReturn("externalDir"); - mockFileHelper(file); - - // mock inputStream - //----------------------------------- - mockInputStreamHelper(); + mockedFileHelperInterface = new MockHelper.MockFileHelper(file); + fileHelper = new FileHelper(mockedFileHelperInterface); + inputStreamHelper = mockInputStreamHelper(); } @@ -217,8 +208,7 @@ public class ProviderManagerTest { providerManager.add(secondCustomProvider); providerManager.saveCustomProvidersToFile(); - verifyStatic(FileHelper.class, times(2)); - FileHelper.persistFile(any(File.class), anyString()); + assertEquals("persist was called twice", 2, mockedFileHelperInterface.getPersistFileCounter()); } diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java index 1f8a18f4..01af9591 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -314,39 +314,6 @@ public class MockHelper { return intent; } - public static void mockTextUtils() { - mockStatic(TextUtils.class); - - when(TextUtils.equals(any(CharSequence.class), any(CharSequence.class))).thenAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - CharSequence a = (CharSequence) invocation.getArguments()[0]; - CharSequence b = (CharSequence) invocation.getArguments()[1]; - if (a == b) return true; - int length; - if (a != null && b != null && (length = a.length()) == b.length()) { - if (a instanceof String && b instanceof String) { - return a.equals(b); - } else { - for (int i = 0; i < length; i++) { - if (a.charAt(i) != b.charAt(i)) return false; - } - return true; - } - } - return false; - } - }); - - when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer() { - @Override - public Boolean answer(InvocationOnMock invocation) throws Throwable { - CharSequence param = (CharSequence) invocation.getArguments()[0]; - return param == null || param.length() == 0; - } - }); - } - public static ResultReceiver mockResultReceiver(final int expectedResultCode) { ResultReceiver resultReceiver = mock(ResultReceiver.class); doAnswer(new Answer() { @@ -398,23 +365,41 @@ public class MockHelper { return resultReceiver; } - public static void mockInputStreamHelper() throws FileNotFoundException { - mockStatic(InputStreamHelper.class); - when(InputStreamHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod(); - when(InputStreamHelper.getInputStreamFrom(anyString())).thenAnswer(new Answer() { + + public static InputStreamHelper mockInputStreamHelper() { + return new InputStreamHelper(new InputStreamHelper.InputStreamHelperInterface() { @Override - public InputStream answer(InvocationOnMock invocation) throws Throwable { - String filename = (String) invocation.getArguments()[0]; - return getClass().getClassLoader().getResourceAsStream(filename); + public InputStream getInputStreamFrom(String filePath) { + return getClass().getClassLoader().getResourceAsStream(filePath); } }); - when(InputStreamHelper.inputStreamToJson(any(InputStream.class))).thenCallRealMethod(); + } + + public static class MockFileHelper implements FileHelper.FileHelperInterface { + private final File file; + private int persistFileCounter = 0; + public MockFileHelper(File file) { + this.file = file; + } + + @Override + public File createFile(File dir, String fileName) { + return file; + } + + @Override + public void persistFile(File file, String content) throws IOException { + persistFileCounter++; + } + + public int getPersistFileCounter() { + return persistFileCounter; + } } - public static void mockFileHelper(final File mockedFile) throws FileNotFoundException { - mockStatic(FileHelper.class); - when(createFile(any(File.class), anyString())).thenReturn(mockedFile); + public static FileHelper mockFileHelper(final File mockedFile) throws FileNotFoundException { + return new FileHelper(new MockFileHelper(mockedFile)); } public static void mockBase64() { @@ -459,7 +444,6 @@ public class MockHelper { when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod(); when(ConfigHelper.parseX509CertificatesFromString(anyString())).thenCallRealMethod(); when(ConfigHelper.getProviderFormattedString(any(Resources.class), anyInt())).thenCallRealMethod(); - when(ConfigHelper.timezoneDistance(anyInt(), anyInt())).thenCallRealMethod(); when(ConfigHelper.isIPv4(anyString())).thenCallRealMethod(); when(ConfigHelper.isDefaultBitmask()).thenReturn(true); when(ConfigHelper.getDomainFromMainURL(anyString())).thenCallRealMethod(); -- cgit v1.2.3