LoginSignup
6
4

More than 1 year has passed since last update.

RustでPrivateメソッドもファイルを分けてテストをしたい。

Last updated at Posted at 2022-08-07

はじめに

Privateメソッドのテスト要否に対する部分は一旦置いとく。
テストしたいぜー、でも、同じファイルに書くのは、テストコードが多くなりすぎて見通し悪くなるから避けたいぜー

みたいな感じで色々試行錯誤してたどり着いた(と言ってもモジュールの可視性とかの話を調べれば一発だったっぽい)
ので備忘録的にメモしているやつでござーます。

とりあえず動かしてみたいという方へ

シンプルにテストを書いてみよう

適当なプロジェクトを作ります。

cargo new testcode_example --lib

トップレベルのフォルダ構成

./
│   .gitignore
│   Cargo.lock
│   Cargo.toml
│
├───src
│       lib.rs
│
└───target
(snip)

それでは、src/lib.rsに以下を追記していきます

src/lib.rs
pub fn pow(x: u32) -> u32 {
    pow_impl(x)
}

fn pow_impl(x: u32) -> u32 {
    x * x
}

#[test]
fn pow_test() {
    let x = pow(2);
    assert_eq!(x, 4)
}
#[test]
fn pow_impl_test() {
    let x = pow_impl(2);
    assert_eq!(x, 4)
}

よいですね。ただ、テストが増えていくと見通しが悪くなってしまいます。
というわけで、テストは分けましょう。

あと、src/lib.rsに書き続けるのも自分の趣味ではないので計算をするので、別モジュール(math)へ集約しようと思います。

テストを分ける前の準備(ファイル分割)

./
│   .gitignore
│   Cargo.lock
│   Cargo.toml
│
├───src
│   │   lib.rs
│   │
│   └───math <-- 新しく作ったディレクトリ
│           exponentiation.rs <-- ここに実装を移しました。
│           mod.rs
└───target
(snip)
math/exponentiation.rs
pub fn pow(x: u32) -> u32 {
    pow_impl(x)
}

fn pow_impl(x: u32) -> u32 {
    x * x
}

#[test]
fn pow_test() {
    let x = pow(2);
    assert_eq!(x, 4)
}
#[test]
fn pow_impl_test() {
    let x = pow_impl(2);
    assert_eq!(x, 4)
}
math/mod.rs
pub mod exponentiation; // モジュールを公開する

代わりにlibにはmathモジュールを使うコードを書いておきます。

src/lib.rs
pub mod math;

fn calc() {
    // 公開されてるAPIなのでOK
    let pow = math::exponentiation::pow(10);
    // 公開されていないAPIなのでビルドエラーが起こる
    let pow_inner = math::exponentiation::pow_impl(10);
    assert_eq!(pow, 100);
}

移植が完了しました。
次は、テストコードも分離していきます。

テストコードの分離(失敗編)

./
│   .gitignore
│   Cargo.lock
│   Cargo.toml
│
├───src
│   │   lib.rs
│   │
│   └───math
│           exponentiation.rs
│           exponentiation_tests.rs <-- テストメソッド群
│           mod.rs
└───target
(snip)
math/exponentiation.rs
pub fn pow(x: u32) -> u32 {
    pow_impl(x)
}

fn pow_impl(x: u32) -> u32 {
    x * x
}
math/exponentiation_test.rs
use crate::math::exponentiation::*;
#[test]
fn pow_test() {
    let x = pow(2);
    assert_eq!(x, 4)
}
#[test]
fn pow_impl_test() {
    let x = pow_impl(2);
    assert_eq!(x, 4)
}
math/mod.rs
pub mod exponentiation;
#[cfg(test)]
pub mod exponentiation_tests; // テストモジュールを公開してcargo testできるようにする

よし、テストをしてみましょう。
image.png

ですよねー!!!!!!
pow_implはexponentiation_tests.rsから見えないよ!って言われてます。
同じファイルには書きたくないし、pubを付けて公開もしたくない・・・どうしよう???

モジュール内での可視性はpubにし、モジュールの可視性をprivateにする(成功)

Rustはファイル単位での可視性を設定できるわけですが
モジュールでの可視性も設定できるというのが自分の中では盲点でした。

math/mod.rs
pub mod exponentiation;
mod exponentiation_impl; // 追加:pubは付けずにモジュールを定義する
#[cfg(test)]
pub mod exponentiation_tests;
math/exponentiation_impl.rs
pub fn pow_impl(x: u32) -> u32 {
    x * x
}
math/exponentiation.rs
use crate::math::exponentiation_impl::*; // 追加
pub fn pow(x: u32) -> u32 {
    pow_impl(x)
}
math/exponentiation_tests.rs
use crate::math::exponentiation::*;
use crate::math::exponentiation_impl::*; // 追加
#[test]
fn pow_test() {
    let x = pow(2);
    assert_eq!(x, 4)
}
#[test]
fn pow_impl_test() {
    let x = pow_impl(2);
    assert_eq!(x, 4)
}
./
│   .gitignore
│   Cargo.lock
│   Cargo.toml
│
├───src
│   │   lib.rs
│   │
│   └───math
│           exponentiation.rs
│           exponentiation_impl.rs <-- テストしたいけど非公開にしたいメソッド
│           exponentiation_tests.rs <-- テストメソッド群
│           mod.rs
└───target
(snip)

めでたく通りましたし、lib.rsで隠蔽されてるimplメソッドを呼ぼうとするとちゃんとエラーになりました。
若干手間ですが、ファイル分離ができてとても幸せな感じですね。

それでは。

6
4
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
6
4