这是对这种方法进行单元测试的好方法吗?

时间:2018-08-19 15:11:27

标签: java unit-testing junit

我需要使用Junit / Mockito测试此方法。 实际上,我无法使用Mockito正确编写测试,因此我使用了自己的输入来代替模拟扫描仪。我不知道我是否做对了。 (任何人都知道如何使用Mockito做到这一点吗?)

这是我要测试的方法:

public RentingACar rentACar(Scanner input) {
    RentingACar rentingACar = new RentingACar();

    System.out.print("Brand: ");
    rentingACar.setBrand(input.next());
    System.out.print("Name: ");
    rentingACar.setName(input.next());
    System.out.print("Surname: ");
    rentingACar.setSurname(input.next());
    System.out.print("Rent Date: ");
    rentingACar.setRentDate(input.next());
    System.out.print("Client number: ");
    rentingACar.setClientNumber(input.nextInt());

    return rentingACar;
}

这是我的考试:

@Test
void rentACar() {
    Scanner scanner = new Scanner("Mazda\nPeter\nParker\n20.02\n1234");
    ClientDataGetter clientDataGetter = new ClientDataGetter();

    RentingACar rentingACar = clientDataGetter.rentACar(scanner);

    assertNotNull(rentingACar);

    assertEquals("Mazda", rentingACar.getBrand());
    assertEquals("Peter", rentingACar.getName());
    assertEquals("Parker", rentingACar.getSurname());
    assertEquals("20.02", rentingACar.getRentDate());
    assertEquals(1234, rentingACar.getClientNumber());
}

这有意义吗?

2 个答案:

答案 0 :(得分:1)

为什么在不需要Mockito时要使用它,地狱,使用Mockito编写代码比没有Mockito时要麻烦得多? Mockito是完全不得已的选择(例如,模拟数据库或外部服务),即使这样,是否应使用它还是值得怀疑的(而不是仅仅编写自己的FakeDatabaseFakeExternalService)。这里绝对不需要,被嘲笑的Scanner还能提供什么帮助?

即使您要测试的代码有很多不足之处,我也会说您的方法很好。因为这个问题是关于测试的,所以我不会谈论代码本身。

您仅测试了所谓的快乐路径,这表示输出完美无缺且一切正常的情况。

您还需要测试边缘情况(输入为最大/最小时,例如L.MIN_VALUE / 0 / Long.MAX_VALUE以数字表示,0以String表示长度等),例如用空/一个字母名称,客户编号以0开头。

毕竟,您需要测试输入是否仅仅是错误的:没有\n符号,没有日期应该是日期的等等,这里有很多可能性。您可能会发现代码还不够,您将不得不对其进行更改(也许添加一些输入验证)。这就是为什么最好从测试(测试驱动的开发)开始的原因,您可以在编写代码之前先看到必须做的验证,然后您将为此做好准备。这是简单的情况,但是向已经存在的代码中添加重要的功能并不总是那么容易,而且如果某人已经使用了该代码,甚至可能会有风险。我见过很多情况,当有人需要进行这样的修改时,但是如果没有巨大的重构是不可能的,所以这就是他们所做的(请不要做):

if(mySpecialCase) {
  // all the logic
} else {
  // all the logic, almost identical to the logic above
}

编辑:回复您的评论-Why is it bad practice to make if conditions?

我在这里不只是在谈论if条件。我说的是两个if分支中潜在的代码重复,它们很可能具有非常相似的代码。假设您要进行另一项更改,您必须在两个分支中都添加另一个if,这会变得一团糟。

If条件也不应过度使用,尤其是当条件归结为许多不同的分支时。仅通过查看代码就很难调试,测试和查看正在发生的事情。一定要有人(或您)通过查看并分析代码来了解代码的作用,这一点非常重要。解决这个问题通常并不容易,需要使用一些design patterns,例如decorator,有时甚至是一个简单的switch也会有很大帮助。

答案 1 :(得分:1)

这就是我使用Mockito编写测试的方式。

import org.junit.Test;
import java.util.Scanner;

import static org.mockito.Mockito.*;

public class ClientDataGetterTest {

    ClientDataGetter clientDataGetter = new ClientDataGetter();

    @Test
    public void testRentACar() {
       // define input data (test Scanner object)
        Scanner scanner = new Scanner("Mazda\nPeter\nParker\n20.02\n1234");

        // call you method
        RentingACar rentACar = spy(new RentingACar());
        clientDataGetter.rentACar(rentACar, scanner);

        // Verify that the methods were called with exact parameter values and exactly one time each
        verify(rentACar, times(1)).setBrand("Mazda");
        verify(rentACar, times(1)).setName("Peter");
        verify(rentACar, times(1)).setSurname("Parker");
        verify(rentACar, times(1)).setRentDate("20.02");
        verify(rentACar, times(1)).setClientNumber(1234);

        // Old code
/*
        //check the results
        assertEquals("Mazda", rentingACar.getBrand());
        assertEquals("Peter", rentingACar.getName());
        assertEquals("Parker", rentingACar.getSurname());
        assertEquals("20.02", rentingACar.getRentDate());
        assertEquals(1234, rentingACar.getClientNumber());
*/
    }

Mockito正在确保您的二传手被叫住。

我个人认为,如果您的RentingACar看起来像一个简单的POJO,那么就不需要像我一样模拟它了,您的方法就可以了。

潜在的更改无效。

您可以通过如下定义Mockito for Scanner://为扫描器定义模拟

Scanner scanner = mock(Scanner.class);
when(scanner.next()).thenReturn("Mazda","Peter","Parker","20.02","1234");

,但是模仿者不能模仿最终课程。标准的java.util.Scanner是最终类。