【问题标题】:How to write a test for a method which is filtering elements from an array based on string criteria如何为基于字符串标准从数组中过滤元素的方法编写测试
【发布时间】:2019-08-26 17:09:24
【问题描述】:

我有一个Car 类,其类别字段为String

public class Car {

     private String category;

     public Car(String category) {
         this.category = category;
     }

     public String getCategory() {
         return category;
     }

     public void setCategory(String category) {
         this.category = category;
     }
}

我有这个方法,它使用contains() 方法从数组中过滤出基于String 的元素,只保留跑车。我知道这是个坏主意,只是尝试编写我的第一个单元测试。你能帮我理解从哪里开始吗?一般来说,测试环路过滤逻辑的最佳实践是什么。

private List<Car> filterOutEverythingExceptSportCars(List<Car> cars) {
    List<Car> filteredCarsList = new ArrayList<>();

    for (int i = 0; i < cars.size(); i++) {
        if (cars.get(i).getCategory().contains("sport")) {
            filteredCarsList.add(cars.get(i));
        }
    }
    return filteredCarsList;
}

【问题讨论】:

  • 请注意,虽然方法标题向我建议您希望将跑车保留在列表中,但代码会过滤掉所有跑车(并保留其余的)。
  • 是的,不好的命名,谢谢你提到它。
  • 实际上,根据您上面的描述,在我看来名称是正确的,但代码与您想要的相反。我相信你应该摆脱 !运算符。
  • 是的,编辑它以避免混淆,谢谢。

标签: java android unit-testing junit tdd


【解决方案1】:

首先,您需要使您的方法可测试。在您的实现中,它是一个私有实例方法。由于您没有使用实例中的任何其他内容,因此可以将其设为包私有静态方法:

static List<Car> filterOutEverythingExceptSportCars(List<Car> cars) {
    List<Car> filteredCarsList = new ArrayList<>();
    for (Car car: cars) {
        if (!car.getCategory().contains("sport")) {
            filteredCarsList.add(car);
        }
    }
    return filteredCarsList;
}

现在您可以轻松编写单元测试,其中包含要比较的输入和输出列表:

@Test
public static void testFilterOutEverythingExceptSportCars() {
    List<Car> cars = ...;
    List<Car> actual = filterOutEverythingExceptSportCars(cars);
    List<Car> expected = ...;
    assertEquals(expected, actual);
}

您只需要静态导入filterOutEverythingExceptSportCars方法并将测试类与实现放在同一个包中即可。

请注意,for 循环的改进取决于您自己的应用,而不是强制性的。我也没有更改命名或 if 条件以匹配。只需编写您的测试并迭代您的实现,直到您最喜欢它...

【讨论】:

    【解决方案2】:

    您可以像这样查看 Stream 并使用过滤器运算符。

    private static List<Car> filterOutSportCars(List<Car> cars) {
       return cars.stream().filter(car -> !car.category.contains("sport")).collect(Collectors.toList());
    
    }
    

    【讨论】:

    • 这是一个不错的快捷方式,谢谢,但我是在问如何为此方法编写测试。
    • 对不起,我误解了这个问题。您可以使用 assertEquals(expected, actual);
    【解决方案3】:

    只需测试合同 - 如果您输入 &lt;something&gt;,那么您期望返回什么?例如,如果我调用filterOutEverythingExceptSportsCars(null),或filterOutEverythingExceptSportsCars(Collections.emptyList()),或filterOutEverythingExceptSportsCars(Arrays.asList(car1, null, car2)) 等会发生什么

    然后围绕这一系列前提条件、操作和预期结果构建您的测试。所以你的第一个测试可能是:

    @Test
    givenListOfCarsIsNull_whenFilteringOutEverythingExceptSportsCars_thenReturnEmptyList() {
        final List<Car> in = null; //precondition
        final List<Car> out = filterOutEverythingExceptSportsCars(in); //calling the method
        assertTrue(out.isEmpty()); //verifying expected output
    }
    

    如果您希望在这种情况下返回一个空列表。相反,您可能希望抛出异常或其他东西。

    【讨论】:

    • 感谢您的回答,但是您如何获得方法filterOutEverythingExceptSportsCars() 的引用并在测试类中调用它?
    • 如果不是static,那么您需要创建它所在的任何类的实例。它的访问级别至少还需要是package-private,而不是private 可以直接测试。
    【解决方案4】:

    也许你想试试kotlin(编译成java字节码):

    import org.assertj.core.api.Assertions.assertThat
    import org.junit.jupiter.api.Test
    
    class Car(var category: String)
    
    fun sportCars(list: List<Car>) = list.filter { it.category.contains("sport") }
    
    class CarTests {
        @Test
        fun `one ferrari and one truck`() {
            val ferrari = Car("ferrarisportcar")
            val truck = Car("truck")
    
            val sportCars = sportCars(listOf(ferrari, truck))
    
            assertThat(sportCars).containsOnly(ferrari)
        }
    }
    

    您可以混合使用 kotlin 和 java。因此,一个选项可能是使用 java 作为生产代码,使用 kotlin 作为测试代码。

    【讨论】:

      猜你喜欢
      • 2021-11-11
      • 1970-01-01
      • 2019-09-11
      • 1970-01-01
      • 2010-10-05
      • 2018-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多