java - Mockito 重复方法调用正在测试的同一函数/构造函数

标签 java junit mockito

这是类Person

public class Person {
    // ...
    public String getInfo(String key)
            return key; //for brevity
    }
}

另一个类Student依赖于Person(请注意,当前实现是问题的一部分,我们无法修改这两个类)

public class Student {
    private String copyFirstName;
    private String otherInfo;

    private Person person;
    Student(Person p) {
        person = p;

        copyFirstName = person.getInfo("firstName")

        if (copyFirstName == null || copyFirstName.equals("")) { //entry point A
            throw new SomethingError("message here");
        }

        otherInfo = person.getInfo("bio")

        if (otherInfo == null || otherInfo.equals("")) { // entry point B
            throw new SomethingError("message here");
        }
    }
}

上面的类可能看起来不切实际,但请将其视为我们无法改变的问题的一部分。

目标是完全覆盖测试中的所有线路。为此,我计划进行两个测试来覆盖两个 if 语句并模拟方法 getInfo,同时注意传递的参数,以便我知道何时跳过 "构造函数第二次测试的入口点 A”。

理想情况下,这将是我的 JUnit 类

public class StudentTest {
    private Person person;
    private Student student;
    private Person mockedPerson;

    @Before
    public void init()
    throws SomethingError  {
        person = new Person();
        student = new Student();
        mockedPerson = Mockito.mock(Person.class);
    }

    @Test(expected=SomethingError.class)
    public void myTest1()
    throws SomethingError {
        Mockito.when(mockedPerson.getInfo("firstName"))
        .thenAnswer(
            new Answer<String>(){
            @Override
            public String answer(InvocationOnMock invocation) {
                String arg = invocation.getArgumentAt(0, String.class);
                System.out.println(arg);
                if (arg.equals("firstName")) {
                    return null;
                }
                return person.getInfo(arg); // default value
            }});

        try {
            new Student(mockedPerson);
            fail();
        } catch (MultilingualException e) {
            Mockito.reset(mockedPerson); // not sure if this works
            assertEquals(e.getMessage(), "message here");
        }
    }

    @Test(expected=SomethingError.class)
    public void myTest2()
    throws SomethingError {
        Mockito.when(mockedPerson.getInfo("bio"))
        .thenAnswer(
            new Answer<String>(){
            @Override
            public String answer(InvocationOnMock invocation) {
                String arg = invocation.getArgumentAt(0, String.class);
                System.out.println(arg);
                if (arg.equals("bio")) {
                    return "";
                }
                return person.getInfo(arg); // defaul value for firstName
            }});

        try {
            new Student(mockedPerson);
            fail();
        } catch (MultilingualException e) {
            Mockito.reset(mockedPerson); // not sure if this works
            assertEquals(e.getMessage(), "message here");
        }
    }
}

但它没有按预期工作。 myTest1 成功输入第一个 if 语句。但随后在 myTest2 上,第二个 if 语句被错过。奇怪的是,myTest2 的 @Override 下面的所有内容都会被错过,并立即进入其 catch 中。

我还尝试创建 Person 的单独模拟实例,但它仍然具有相同的覆盖结果。

如何测试并覆盖构造函数中从同一方法获取评估值的两个连续 if 语句?

编辑

我确实尝试了下面最简单的方法,但似乎 .when 并不关心参数的值是什么,因为第二个测试仍然触发第一个 if .

@Test(expected=SomethingError.class)
public void test() throws SomethingError {
    Mockito.when(mockedPerson.getInfo("firstName")).thenReturn(null);
    try {
        new Student(mockedPerson);
    } catch (SomethingError e) {
        assertEquals(e.getMessage(), "message here");
    }
}

@Test(expected=SomethingError.class)
public void test2() throws SomethingError {
    Mockito.when(mockedPerson.getInfo("bio")).thenReturn(null);
    try {
        new Student(mockedPerson);
    } catch (SomethingError e) {
        assertEquals(e.getMessage(), "message here");
    }
}

最佳答案

不需要那个私有(private)学生;,实际上你可以摆脱测试类中的所有这些字段,你的测试应该尽可能独立,在你的情况下你可以在每个测试方法中一起进行模拟和 stub 。 使用

ArgumentMatchers.eq("firstName");

要检查参数值是否相等,如果需要,您也可以使用答案变体,但在您的情况下,这要简单得多。

以下是您的测试的样子:

@Test
public void newStudentWithPersonWithNullFirstName() {

    Person person = Mockito.mock(Person.class);

    Mockito
        .doReturn(null)
        .when(person)
        .getInfo(ArgumentMatchers.eq("firstName"));

    SomethingError actual = Assert
            .assertThrows(SomethingError.class, () -> new Student(person));
    Assert.assertEquals("firstName is null or empty", actual.getMessage());
}

@Test
public void newStudentWithPersonWithEmptyFirstName() {

    Person person = Mockito.mock(Person.class);

    Mockito
        .doReturn("")
        .when(person)
        .getInfo(ArgumentMatchers.eq("firstName"));

    SomethingError actual = Assert
            .assertThrows(SomethingError.class, () -> new Student(person));
    Assert.assertEquals("firstName is null or empty", actual.getMessage());
}

@Test
public void newStudentWithPersonWithNullBio() {

    Person person = Mockito.mock(Person.class);

    Mockito
        .doReturn("Foo")
        .when(person)
        .getInfo(ArgumentMatchers.eq("firstName"));

    Mockito
        .doReturn(null)
        .when(person)
        .getInfo(ArgumentMatchers.eq("bio"));

    SomethingError actual = Assert
            .assertThrows(SomethingError.class, () -> new Student(person));
    Assert.assertEquals("bio is null or empty", actual.getMessage());
}

@Test
public void newStudentWithPersonWithEmptyBio() {

    Person person = Mockito.mock(Person.class);

    Mockito
        .doReturn("Foo")
        .when(person)
        .getInfo(ArgumentMatchers.eq("firstName"));

    Mockito
        .doReturn("")
        .when(person)
        .getInfo(ArgumentMatchers.eq("bio"));

    SomethingError actual = Assert
            .assertThrows(SomethingError.class, () -> new Student(person));
    Assert.assertEquals("bio is null or empty", actual.getMessage());
}

@Test
public void newStudentWithPersonSuccess() {

    Person person = Mockito.mock(Person.class);

    Mockito
        .doReturn("Foo")
        .when(person)
        .getInfo(ArgumentMatchers.eq("firstName"));

    Mockito
        .doReturn("Bar")
        .when(person)
        .getInfo(ArgumentMatchers.eq("bio"));

    Student actual = new Student(person);
    Assert.assertEquals("Foo", actual.getCopyFirstName());
    Assert.assertEquals("Bar", actual.getOtherInfo());
}

覆盖范围:

enter image description here

关于java - Mockito 重复方法调用正在测试的同一函数/构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60929781/

相关文章:

java - 验证 PUT 操作的资源

java - 向 Java 解析器/词法分析器发出指令

java - SpringBoot @interface 总结不同注解

java - 我们应该测试 getForObject 吗?

java - Mockito:模拟 Hibernate Criteria uniqueResult 时出现 NullPointerException

java - 我应该设置时区将字符串转换为日期吗?

java - 如何在方法中写入一些 'pause'?

java - org.mockito.Mockito.times(1) 有方便的方法吗?

java - 在单元测试中验证私有(private)方法调用的顺序

java - 在 Spring 的构建后阶段之前注入(inject) mock