0

Let's say there is a function to determine if a button should be visible.

fun isButtonVisible(fitlers: List<Filters>, results: List<Shop>, isLoading: Boolean) {
  return fitlers.isNotEmpty() && results.isEmpty() && !isLoading
}

Now I would like to test this function using PBT like:

"the button should be visible if filters is not empty and results is empty and is not loading" {
  forAll { filters: List<Filters>, results: List<Shop>, isLoading: Boolean ->
    val actual = isButtonVisible(filters, results, isLoading)

    // Here reimplement the logic
    val expected = filters.isNotEmpty() && results.isEmpty() && !isLoading

    assertThat(actual).isEqual(expected)
  }
}

It seems that I just reimplement the logic again in my test, is this correct? If not, how can I come up with another properties if the logic is just simple combinations of several flags?

jaychang0917
  • 1,880
  • 1
  • 16
  • 21
  • You can reimplement but I don't suggest it. Instead you should work out the result and hard code it. So `assertThat(actual).isEqual(true)` if it should be true. – CodingYoshi Jun 02 '19 at 15:39
  • Can you give an example? In this case, I have no idea what another properties that I can use except reimplementing it. – jaychang0917 Jun 02 '19 at 15:48
  • I gave an example already. I am not sure what you're asking. – CodingYoshi Jun 02 '19 at 15:52
  • "work out the result and hard code it" <-- you means I should use example based test to hard code 8 possible cases? If so, why is it better than reimplementing it? Isn't PBT better in this case if there are many combinations? – jaychang0917 Jun 03 '19 at 01:21
  • then each combination becomes a separate test – Andrei Dragotoniu Jun 03 '19 at 12:39
  • 1
    It's better than reimplementing because in some cases the implementation is very complex so the reimplementation can have bugs. You don't need 8 different tests; look into parameterized tests. – CodingYoshi Jun 03 '19 at 13:42

1 Answers1

0

that is not right.

you should not have to calculate what the expected value should be during the test, you should know what the result should be, set it as such and compare it against the actual result.

Tests work by calling the method you want to test and comparing the result against an already known, expected value.

"the button should be visible when filters are not empty, results is empty, isLoading is false " {
  forAll { filters: List<Filters>, results: List<Shop>, isLoading: Boolean ->
    val actualVisibleFlag = isButtonVisible(filters, results, isLoading)

    val expectedVisibleFlag = true    
    assertThat(actualVisibleFlag ).isEqual(expectedVisibleFlag )
  }
}

Your expected value is known, this is the point I am trying to make. For each combination of inputs, you create a new test.

The idea here is that when you have a bug you can easily see which existing test fails or you can add a new one which highlights the bug.

If you call a method, to give you the result of what you think you should get, well, how do you know that method is correct anyway? How do you know it works correctly for every combination?

You might get away with less tests if you reduce your number of flags maybe, do you really need 4 of them?

Now, each language / framework has ( or should have ) support for a matrix kind of thing so you can easily write the values of every combination

Andrei Dragotoniu
  • 6,155
  • 3
  • 18
  • 32