1 minute read

StreamData는 값을 생성하는 Elixir 라이브러리다. Property-based testing을 할 때, 주로 사용하고 있다.

유저 정보를 외부에서 읽는 걸 테스트한다고 치자. 0에서 10명의 정보를 읽고 이 정보 안에서 이상한 데이터가 포함된 걸 테스트하고 싶다. 이상한 데이터 수는 유저 데이터 수보다 같거나 적어야 한다.

즉, [0, 10] 범위에서 랜덤한 수를 하나 뽑고 [0, 뽑은 수] 사이에서 하나를 더 뽑아 이상한 데이터 수를 만들어야 한다. 이렇게 생성기(generator)를 조합하고 싶을 때, StreamData.bind/2 함수를 사용할 수 있다.

user_count, invalid_user_count 생성기를 정의해본다.

user_count = StreamData.integer(0..10)
invalid_user_count = StreamData.bind(user_count, fn c -> StreamData.integer(0..c) end)
user_count |> Enum.take(1)
#=> [4]
invalid_user_count |> Enum.take(1)
#=> [5]

의도와 다르게 invalid_user_count가 user_count 보다 크다. StreamData가 골라낸 랜덤한 수를 사용해야 하는데, [0, 10] 범위의 수 중에 하나를 생성하는 생성기 자체를 바인딩해서 그렇다. 즉, user_data도 [0, 10] 사이 수를 하나 생성하고 invalid_user_count는 [0, 10] 사이 수를 생성하고 다시 [0, 생성한 수] 사이에서 하나를 생성하기 때문이다.

StreamData.bind/2 함수에서 user_count와 invalid_user_count를 둘 다 생성하게 고쳐보자.

data = StreamData.bind(
StreamData.integer(0..10),
fn data -> {StreamData.constant(data), StreamData.integer(0..data)} end)

[0, 10] 사이 수를 생성한다. 튜플(tuple) 첫 번째 요소로 생성한 수 자체를 사용하고 두 번째 요소로는 [0, 생성한 수] 사이 수를 생성하게 고친다.

data |> Enum.take(1)
#=> [{3, 0}]
data |> Enum.take(1)
#=> [{8, 6}]
data |> Enum.take(1)
#=> [{9, 4}]

의도한대로 동작한다. 생성한 값이 아니라 생성기 자체를 StreamData.bind/2 함수에 넘겨서 다시 한번 더 생성하게 해서 의도한 대로 동작하지 않았다.