测试是否调用了另一种方法

时间:2021-08-06 16:04:52

So I'm sure there is something like this out there but I have been searching for an hour and haven't found exactly what I am looking for. say I have a class that looks like this:

所以我确定那里有类似的东西,但我一直在寻找一个小时,并没有找到我正在寻找的东西。说我有一个看起来像这样的课:

public class MyClass
{
    public void myMethod(boolean shouldCallOtherMethod)
    {
        if(shouldCallOtherMethod)
        {
            otherMethod();
        }
    }

    public void otherMethod()
    {
        System.out.println("Called");
    }
}

How do I make something like this work?

我如何制作这样的作品呢?

@Test
public void shouldCallMethod()
{
    MyClass myClass = new MyClass();
    myClass.myMethod(true)

    // verify myClass.otherMethod method was called
}

4 个解决方案

#1


11  

Using Mockito, you can do spying on real objects like this:

使用Mockito,你可以对这样的真实物体进行间谍活动:

import org.junit.Test;
import static org.mockito.Mockito.*;
public class MyClassTest {
    @Test
    public void otherMethodShouldBeCalled() {
        MyClass myClass = new MyClass();
        MyClass spy = spy(myClass);

        spy.myMethod(true);
        verify(spy).otherMethod();
    }
}

There are some gotchas, so take a look at the relevant documentation as well.

有一些问题,所以也看一下相关的文档。

#2


5  

Suppose MokeysClass has a constructor declared like this, where Foo is some other class.

假设MokeysClass有一个像这样声明的构造函数,其中Foo是其他类。

public MokeysClass(String name, int counter, Foo myFoo)

I would write my test like this.

我会像这样写我的测试。

@RunWith(MockitoJUnitRunner.class)
public class TestArray {
    @Mock 
    private Foo mockMyFoo;
    private String nameToInject = "Mokey";
    private int counterToInject = 42;

    @Spy 
    private MokeysClass toTest = new MokeysClass(nameToInject, counterToInject, mockMyFoo);

    @Test
    public void shouldCallMethod() {
        toTest.myMethod(true);
        verify(toTest).otherMethod();
    }
}

so that I am explicitly stating which constructor to call when I create my test object, and what arguments to pass to it.

所以我明确说明在创建测试对象时要调用哪个构造函数,以及传递给它的参数。

There are some reasons not to rely on @InjectMocks to do this step for me, particularly if the class being tested is more complex and has more than one constructor. Mockito chooses the constructor that has the most arguments, but if there are several constructors with the same number of arguments, Mockito could choose any of the constructors; that is, the behaviour is undefined.

有一些原因不依赖@InjectMocks为我执行此步骤,特别是如果被测试的类更复杂并且具有多个构造函数。 Mockito选择具有最多参数的构造函数,但如果有多个构造函数具有相同数量的参数,Mockito可以选择任何构造函数;也就是说,行为是未定义的。

Once Mockito has chosen a constructor, it checks whether that constructor can in fact be used for constructor injection. Constructor injection will not be used if

一旦Mockito选择了构造函数,它就会检查该构造函数是否实际上可以用于构造函数注入。如果,将不使用构造函数注入

  • one or more of the parameters of the chosen constructor is a primitive type,
  • 所选构造函数的一个或多个参数是基本类型,
  • the type of one or more of the parameters of the chosen constructor is a final class,
  • 所选构造函数的一个或多个参数的类型是最终类,
  • the type of one or more of the parameters of the chosen constructor is a private class,
  • 所选构造函数的一个或多个参数的类型是私有类,
  • the only constructor of the class is the default constructor.
  • 该类的唯一构造函数是默认构造函数。

If any one of these conditions holds, for the constructor that Mockito chose, then constructor injection won’t be used. In this case, the class must have a default constructor, otherwise Mockito will throw an exception.

如果这些条件中的任何一个成立,对于Mockito选择的构造函数,则不会使用构造函数注入。在这种情况下,类必须具有默认构造函数,否则Mockito将抛出异常。

The complexity of the criteria which Mockito uses when choosing whether to apply constructor injection implies that adding or removing a constructor, or changing the parameters of a constructor, can make Mockito switch from using constructor injection to using setter and field injection; or from using setter and field injection to using constructor injection. This can occur even if the constructor that is changed is not the one that will be used for constructor injection.

Mockito在选择是否应用构造函数注入时使用的标准的复杂性意味着添加或删除构造函数或更改构造函数的参数可以使Mockito从使用构造函数注入切换到使用setter和field注入;或者使用setter和field injection来使用构造函数注入。即使更改的构造函数不是将用于构造函数注入的构造函数,也会发生这种情况。

As a result, any test that uses constructor injection is automatically quite brittle; in the sense that changes that are not directly related to the test itself can cause the test to fail. Such failures can be difficult to troubleshoot.

因此,任何使用构造函数注入的测试都会自动变得非常脆弱;从某种意义上说,与测试本身没有直接关系的变化会导致测试失败。这样的故障可能很难排除故障。

The @InjectMocks annotation was designed for use with frameworks such as Spring that do dependency injection; and for tests of classes that use Spring, it can be invaluable. But if dependency injection is not part of your class, I would strongly recommend avoiding @InjectMocks on account of its brittleness. You really want your test code to be as easy to maintain and to troubleshoot as your production code is.

@InjectMocks注释被设计用于执行依赖注入的Spring等框架;对于使用Spring的类的测试,它可能是非常宝贵的。但是如果依赖注入不属于你的类,我强烈建议避免使用@InjectMocks,因为它的脆弱性。您确实希望您的测试代码像生产代码一样易于维护和排除故障。

#3


3  

This is not recommended, but you can spy real object :)

这是不推荐的,但你可以窥探真实的对象:)

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.BDDMockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Spy
    private MyClass sut; // System Under Test

    @Test
    public void shouldCallMethod() {

        // when
        sut.myMethod(true);

        // then
        verify(sut).otherMethod();
    }
}

Result:

结果:

Tests Passed: 1 passed in 0,203 s

After changing code: sut.myMethod(false);

更改代码后:sut.myMethod(false);

Wanted but not invoked:
sut.otherMethod();
-> at my.custom.MyClassTest.shouldCallMethod(MyClassTest.java:23)

Source: Spying on real objects

来源:窥探真实物体


Magic version with constructor injection

@Mock
private LexAnalyzer lexAnalyzer;

@Spy
@InjectMocks
private SyntaxAnalyzer sut; // System Under Test

@Test
public void shouldCallMethod() {

    // when
    sut.myMethod(true);

    // then
    verify(sut).otherMethod();
}

SyntaxAnalyzer.java

public class SyntaxAnalyzer {

    private final LexAnalyzer lexAnalyzer;

    public SyntaxAnalyzer(LexAnalyzer lexAnalyzer) {
        this.lexAnalyzer = lexAnalyzer;
    }
...

Tested, works ;)

经过测试,有效;)

#4


2  

I think you want to look at Mock objects. You can create a mock of MyClass, then set expectations that otherMethod() is called when you call myMethod and fails if it was not called.

我想你想看看Mock对象。您可以创建MyClass的模拟,然后设置在调用myMethod时调用otherMethod()的期望,如果未调用则调整失败。

Here is a pretty good overview of them for java - http://www.scalatest.org/user_guide/testing_with_mock_objects

以下是对java的非常好的概述 - http://www.scalatest.org/user_guide/testing_with_mock_objects

One other major benefit of using Mocks, you can avoid side affects, like logging to NSLog or hitting a web server or printing) in your test.

使用Mocks的另一个主要好处是,您可以避免在测试中产生副作用,例如记录到NSLog或访问Web服务器或打印。

#1


11  

Using Mockito, you can do spying on real objects like this:

使用Mockito,你可以对这样的真实物体进行间谍活动:

import org.junit.Test;
import static org.mockito.Mockito.*;
public class MyClassTest {
    @Test
    public void otherMethodShouldBeCalled() {
        MyClass myClass = new MyClass();
        MyClass spy = spy(myClass);

        spy.myMethod(true);
        verify(spy).otherMethod();
    }
}

There are some gotchas, so take a look at the relevant documentation as well.

有一些问题,所以也看一下相关的文档。

#2


5  

Suppose MokeysClass has a constructor declared like this, where Foo is some other class.

假设MokeysClass有一个像这样声明的构造函数,其中Foo是其他类。

public MokeysClass(String name, int counter, Foo myFoo)

I would write my test like this.

我会像这样写我的测试。

@RunWith(MockitoJUnitRunner.class)
public class TestArray {
    @Mock 
    private Foo mockMyFoo;
    private String nameToInject = "Mokey";
    private int counterToInject = 42;

    @Spy 
    private MokeysClass toTest = new MokeysClass(nameToInject, counterToInject, mockMyFoo);

    @Test
    public void shouldCallMethod() {
        toTest.myMethod(true);
        verify(toTest).otherMethod();
    }
}

so that I am explicitly stating which constructor to call when I create my test object, and what arguments to pass to it.

所以我明确说明在创建测试对象时要调用哪个构造函数,以及传递给它的参数。

There are some reasons not to rely on @InjectMocks to do this step for me, particularly if the class being tested is more complex and has more than one constructor. Mockito chooses the constructor that has the most arguments, but if there are several constructors with the same number of arguments, Mockito could choose any of the constructors; that is, the behaviour is undefined.

有一些原因不依赖@InjectMocks为我执行此步骤,特别是如果被测试的类更复杂并且具有多个构造函数。 Mockito选择具有最多参数的构造函数,但如果有多个构造函数具有相同数量的参数,Mockito可以选择任何构造函数;也就是说,行为是未定义的。

Once Mockito has chosen a constructor, it checks whether that constructor can in fact be used for constructor injection. Constructor injection will not be used if

一旦Mockito选择了构造函数,它就会检查该构造函数是否实际上可以用于构造函数注入。如果,将不使用构造函数注入

  • one or more of the parameters of the chosen constructor is a primitive type,
  • 所选构造函数的一个或多个参数是基本类型,
  • the type of one or more of the parameters of the chosen constructor is a final class,
  • 所选构造函数的一个或多个参数的类型是最终类,
  • the type of one or more of the parameters of the chosen constructor is a private class,
  • 所选构造函数的一个或多个参数的类型是私有类,
  • the only constructor of the class is the default constructor.
  • 该类的唯一构造函数是默认构造函数。

If any one of these conditions holds, for the constructor that Mockito chose, then constructor injection won’t be used. In this case, the class must have a default constructor, otherwise Mockito will throw an exception.

如果这些条件中的任何一个成立,对于Mockito选择的构造函数,则不会使用构造函数注入。在这种情况下,类必须具有默认构造函数,否则Mockito将抛出异常。

The complexity of the criteria which Mockito uses when choosing whether to apply constructor injection implies that adding or removing a constructor, or changing the parameters of a constructor, can make Mockito switch from using constructor injection to using setter and field injection; or from using setter and field injection to using constructor injection. This can occur even if the constructor that is changed is not the one that will be used for constructor injection.

Mockito在选择是否应用构造函数注入时使用的标准的复杂性意味着添加或删除构造函数或更改构造函数的参数可以使Mockito从使用构造函数注入切换到使用setter和field注入;或者使用setter和field injection来使用构造函数注入。即使更改的构造函数不是将用于构造函数注入的构造函数,也会发生这种情况。

As a result, any test that uses constructor injection is automatically quite brittle; in the sense that changes that are not directly related to the test itself can cause the test to fail. Such failures can be difficult to troubleshoot.

因此,任何使用构造函数注入的测试都会自动变得非常脆弱;从某种意义上说,与测试本身没有直接关系的变化会导致测试失败。这样的故障可能很难排除故障。

The @InjectMocks annotation was designed for use with frameworks such as Spring that do dependency injection; and for tests of classes that use Spring, it can be invaluable. But if dependency injection is not part of your class, I would strongly recommend avoiding @InjectMocks on account of its brittleness. You really want your test code to be as easy to maintain and to troubleshoot as your production code is.

@InjectMocks注释被设计用于执行依赖注入的Spring等框架;对于使用Spring的类的测试,它可能是非常宝贵的。但是如果依赖注入不属于你的类,我强烈建议避免使用@InjectMocks,因为它的脆弱性。您确实希望您的测试代码像生产代码一样易于维护和排除故障。

#3


3  

This is not recommended, but you can spy real object :)

这是不推荐的,但你可以窥探真实的对象:)

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.BDDMockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Spy
    private MyClass sut; // System Under Test

    @Test
    public void shouldCallMethod() {

        // when
        sut.myMethod(true);

        // then
        verify(sut).otherMethod();
    }
}

Result:

结果:

Tests Passed: 1 passed in 0,203 s

After changing code: sut.myMethod(false);

更改代码后:sut.myMethod(false);

Wanted but not invoked:
sut.otherMethod();
-> at my.custom.MyClassTest.shouldCallMethod(MyClassTest.java:23)

Source: Spying on real objects

来源:窥探真实物体


Magic version with constructor injection

@Mock
private LexAnalyzer lexAnalyzer;

@Spy
@InjectMocks
private SyntaxAnalyzer sut; // System Under Test

@Test
public void shouldCallMethod() {

    // when
    sut.myMethod(true);

    // then
    verify(sut).otherMethod();
}

SyntaxAnalyzer.java

public class SyntaxAnalyzer {

    private final LexAnalyzer lexAnalyzer;

    public SyntaxAnalyzer(LexAnalyzer lexAnalyzer) {
        this.lexAnalyzer = lexAnalyzer;
    }
...

Tested, works ;)

经过测试,有效;)

#4


2  

I think you want to look at Mock objects. You can create a mock of MyClass, then set expectations that otherMethod() is called when you call myMethod and fails if it was not called.

我想你想看看Mock对象。您可以创建MyClass的模拟,然后设置在调用myMethod时调用otherMethod()的期望,如果未调用则调整失败。

Here is a pretty good overview of them for java - http://www.scalatest.org/user_guide/testing_with_mock_objects

以下是对java的非常好的概述 - http://www.scalatest.org/user_guide/testing_with_mock_objects

One other major benefit of using Mocks, you can avoid side affects, like logging to NSLog or hitting a web server or printing) in your test.

使用Mocks的另一个主要好处是,您可以避免在测试中产生副作用,例如记录到NSLog或访问Web服务器或打印。