LoginSignup
2
0

More than 3 years have passed since last update.

Contextを受け取ってファイルを読み書きするクラスをテストする

Last updated at Posted at 2019-04-14

コンストラクタでContextを受け取って、Context.openFileInputContext.openFileOutputを利用するクラスのテストコードの書き方にハマったので書いておきます。

テスト対象のクラス

BooleanRepository.java
public class BooleanRepository {
    static final String FILE_NAME = "BOOLEAN.txt";

    @NonNull private final Context context;

    public BooleanRepository(@NonNull Context context) { this.context = context; }

    public boolean load() throws IOException {
        try (final InputStream is = context.openFileInput(FILE_NAME);
             final InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
             final BufferedReader reader = new BufferedReader(isr)) {
            return Boolean.valueOf(reader.readLine());
        }
    }

    public void save(boolean bool) throws IOException {
        try (final OutputStream os = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
             final OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
             final PrintWriter writer = new PrintWriter(osw)) {
            writer.append(bool);
        }
    }
}

結論 Robolectricを使う

Robolectricを使うことで、Androidに依存するテストもJVM上で実行することができます。

build.gradle
android {
    compileSdkVersion 28
    ...
    testOptions {
        unitTests {
            includeAndroidResources = true
        }
    }
}

dependencies {
    testImplementation 'androidx.test:core:1.2.0'
    testImplementation 'com.google.truth:truth:0.45'
}

BooleanRepositorySpec.java
@RunWith(RobolectricTestRunner.class)
public class MemoRepositorySpec {
    private static final boolean INPUT_BOOL = true;
    private static final String INPUT_STRING = String.valueOf(INPUT_BOOL);

    private BooleanRepository booleanRepository;
    private Context context;

    @Before
    public void setUp() {
        this.context = ApplicationProvider.getApplicationContext();
        this.booleanRepository = new BooleanRepository(context);
    }

    @Test
    public void load() throws Exception {
        final File file = new File(context.getFilesDir(), BooleanRepository.FILE_NAME);
        try (final Writer fileWriter = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
            fileWriter.write(INPUT_STRING);
        }

        final boolean output = booleanRepository.load();
        assertThat(output).isEqualTo(INPUT_BOOL);
    }

    @Test
    public void save() throws Exception {
        booleanRepository.save(true);

        final File file = new File(context.getFilesDir(), BooleanRepository.FILE_NAME);
        try (final FileInputStream fileInputStream = new FileInputStream(file)) {
            final byte[] readBuffer = new byte[INPUT_STRING.length()];
            fileInputStream.read(readBuffer);
            assertThat(readBuffer).isEqualTo(INPUT_STRING);
        }
    }
}

以下蛇足

試したこと1 BufferedReaderPrintWriterをモックする

最初にメソッド内でnewしているBufferedReaderPrintWriterPowerMockでモックすれば良いのでは?と考えた。
しかし、PowerMockの使い方がわからずうまく置き換わらず断念。

試したこと2 Context.openFileInputContext.openFileOutputをモックする

次にContext.openFileInputContext.openFileOutputByteArrayInputStreamByteArrayOutputStreamを返すようにモックできれば良いのでは?と考えた。
しかしContext.openFileInputContext.openFileOutputの戻り値はそれぞれFileInputStreamFileOutputStreamなので戻り値が合わないので無理。

試したこと3 InputStreamOutputStreamを返すメソッドを作り、それをモックする

InputStreamOutputStreamを返すメソッドを作って、

BooleanRepository.java
    InputStream getInputStream() throws FileNotFoundException {
        return context.openFileInput(FILE_NAME);
    }

    OutputStream getOutputStream() throws FileNotFoundException {
        return context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
    }

テスト対象のメソッドをそこから読み取るように変更する。

BooleanRepository.java
        try (final InputStream is = getInputStream();
        /* ... */
        try (final OutputStream os = getOutputStream();

そのメソッドをMockitoでモックし、それぞれByteArrayInputStreamByteArrayOutputStreamを返すようにする。
ByteArrayInputStreamにはファイルの内容のbyte配列をコンストラクタに渡せる。
ByteArrayOutputStreamなら.toByteArray()すると出力内容をbyte[]で読み取れる。

BooleanRepositorySpec.java
public class MemoRepositorySpec {
    private static final boolean INPUT_BOOL = true;
    private static final byte[] INPUT_BYTES = String.valueOf(INPUT_BOOL).getBypes(StandardCharsets.UTF_8);

    @Spy
    private BooleanRepository booleanRepository;
    private Context context;

    @Before
    public void setUp() {
        context = mock(Context.class);
        booleanRepository = new BooleanRepository(context);
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void load() throws Exception {
        final ByteArrayInputStream is = new ByteArrayInputStream(INPUT_BYTES);
        doReturn(is).when(booleanRepository).getInputStream();

        final boolean output = booleanRepository.load();
        assertThat(output).isEqualTo(INPUT_BOOL);
    }

    @Test
    public void save() throws Exception {
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        doReturn(os).when(booleanRepository).getOutputStream();

        booleanRepository.save(true);
        assertThat(os.toByteArray()).containsExactly(INPUT_BYTES);
    }
}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0