`@Autowired`字段在使用Junit进行测试时为null,如何进行模拟/实例化?

huangapple go评论126阅读模式
英文:

The @Autowired field is null during tests with Junit, how can i mock/instantiate it?

问题

我无法访问 `MyClass2` 的代码,也无法更改它。我该如何对 `MyClass2 myClass2` 进行模拟/实例化?

类和代码测试:

@RunWith(JUnit4.class)//无法更改此部分
public class MyTest
{
    @Autowired // 或者不使用,两种方式都尝试过
    MyClass testedInstance = new MyClass();
    
    @Test
    public void boot() throws Exception{
        testedInstance.boot();
        assertTrue(true);
    }
}

public class MyClass
{
    @Autowired 
    private MyClass2 myClass2;
    
    void boot()
    {
        myClass2.foo();//在这里得到空指针异常
    }
}
英文:

I Have no access to MyClass2 code and can't change it. How do i mock/instantiate MyClass2 myClass2?

Classes and code test:

@RunWith(JUnit4.class)//can't change thisone
public class MyTest
{
	@Autowired // or not, tried both ways
	MyClass testedInstance= new MyClass();
	@Test
	public void boot() throws Exception{
		testedInstance.boot();
		assertTrue(true);
	}
}

public class MyClass
{
	@Autowired 
	private MyClass2 myClass2;

	void boot()
	{
		myClass2.foo();//getting a null pointer here
	}
}

答案1

得分: 1

以下是翻译好的部分:

你可以查看原始回答:
https://stackoverflow.com/a/71591567/5108695

但这是一个非常常见的问题,所以我也在这里发布了答案。

在我看来,我们正在编写单元测试用例,我们不应该初始化 Spring 上下文来测试一段代码。

所以,

我使用 Mockito 来模拟我的主要目标测试类中的 Autowired beans,并将这些模拟的 beans 注入到我的主要测试类对象中。

可能听起来有点混乱,看下面的示例 👇

我使用的依赖项:

testImplementation("org.mockito:mockito-core:2.28.2")
testImplementation("org.mockito:mockito-inline:2.13.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")

我的主类是 Maths,并且自动装配了 Calculator bean。

class Maths {

   @Autowired Calculator cal;

   // .........
   // .........

   public void randomAddMethod() {
      cal.addTwoNumbers(1, 2); // 将返回 3;
   }
}

测试类:

@ExtendWith(MockitoExtension.class)
class MathsTest {

   @Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;

   @InjectMocks Maths maths = new Maths();

   @Test
   void testMethodToCheckCalObjectIsNotNull() {
      maths.randomAddMethod();
   }
}

现在,在 Maths 类中,cal 将不会为 null,并且将按预期工作。

英文:

you can check the original answer

https://stackoverflow.com/a/71591567/5108695

But this is very common problem so I posted the answer here also

In my opinion, we are writing unit test cases and we should not initialize the spring context in order to test a piece of code.

So,

I used Mockito to mock the Autowired beans in my main target test class and injected those mock beans in my main test class Object

maybe sounds confusing, see the following example 💥

Dependencies I used

    testImplementation("org.mockito:mockito-core:2.28.2")
    testImplementation("org.mockito:mockito-inline:2.13.0")
    testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
    testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")

My main class is Maths and Calculator bean is autowired


class Maths{

   @Autowired Calculator cal;

   .........
   .........

   public void randomAddMethod(){
      cal.addTwoNumbers(1,2); // will return 3;
   }
}

Test class


@ExtendWith(MockitoExtension.class)

class MathsTest{

   @Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;

   @InjectMocks Maths maths = new Maths();

   @Test testMethodToCheckCalObjectIsNotNull(){
      maths.randomAddMethod();
   }
}

Now cal will not be null in Maths class and will work as expected

答案2

得分: 0

首先,您需要使用以下方式为测试类添加注解:

@RunWith(SpringJUnit4ClassRunner.class)

然后关于 MyClass:

@Autowired
MyClass testedInstance;

您需要删除 = new MyClass();,因为您正在进行自动装配。

然后,由于现在您正在注入 MyClass,如果存在可以注入的 MyClass2 实例,它将被注入到 MyClass 中,如果没有,就不会注入。

您需要配置应用程序上下下文,以便存在名为 MyClass2 的这样的 Bean。

英文:

First you need to annotate your test class with:

@RunWith( SpringJUnit4ClassRunner.class )

Then about MyClass:

@Autowired 
MyClass testedInstance;

you need to delete the = new MyClass(); because you are autowiring it.

Then since now you are injecting MyClass if there is available for injection instance of MyClass2 it will be injected in MyClass, if not it will not.

You need to configure your application context so that such bean MyClass2 exists,

答案3

得分: 0

如果您在测试中使用实现类作为自动装配的目的,但操作代码使用接口,并且存在代理,那么可能会出现问题。例如,如果您在使用Spring Security的@PreAuthorize注解或者Spring AOP的切入点。我不能百分之百确定这是否会发生在CGLIB代理中,但绝对会在JDK代理中发生。

您必须获得确切正确的bean,否则事情将无法正常工作,由于正在测试的bean处于半配置状态,您将会收到非常奇怪(“不可能的”)空指针异常。

英文:

Something that can bite is if you're using the implementation class for autowiring purposes in tests, but the operational code is using an interface instead and there is proxying. This can happen if, for example, you are using @PreAuthorize annotations from Spring Security, or pointcuts from Spring AOP. I'm not 100% sure if this happens with CGLIB proxies, but it definitely happens with JDK proxies.

You have to get exactly the correct bean or things will not work and you will get very strange ("impossible") null pointer exceptions due to the bean you're testing being in a half-configured state.

huangapple
  • 本文由 发表于 2020年4月6日 22:52:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/61062570.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定