首页 > 解决方案 > 在 Autowired 之前如何配置模拟?

问题描述

我想在测试之前使用 Mockito 设置模拟数据。但是,自动装配发生在之前,@Before所以我的预期数据存在的测试丢失了。有办法解决吗?

@RunWith(SpringRunner.class)
@ContextConfiguration(
    classes = {
       Foo.class
    }
)
public class FooTest {
    @MockBean
    final Programs programs;
    @Autowired
    final Foo foo;

    @Before
    public void setPrograms() {

        when(programs.findAll())
            .thenReturn(
                List.of(
                    "A", "B", "C"
                )
            );

    }

    @Test
    public void foo() {
      assertThat(foo.getBlah()).isNotEmpty();
    }
}

interface Programs {
  List<String> findAll();
}

class Foo {

  // I have more complicated structures than a list, for example only.
  private List<String> blah;
  @Autowired
  private Programs programs;

  public List<String> getBlah() { return blah; }

  @PostConstruct
  public void init() {
    blah = programs.findAll();
  }
}

标签: springtestingmocking

解决方案


按照你的例子,我开发了下一个:

public interface Programs {
  List<String> findAll();
}


public class Foo {

  // I have more complicated structures than a list, for example only.
  private List<String> blah;

  @Autowired
  private Programs programs;

  public List<String> getBlah() { return blah; }

  @PostConstruct
  public void init() {
    blah = programs.findAll();
  }
}

以上等于您提供的代码。现在,“变化”:

// Only for testing purpose, you can use any of your own ones
public class ProgramsImpl implements Programs {

  @Override
  public List<String> findAll() {
    return asList("1", "2", "3");
  }
}

和junit测试:

@RunWith(SpringRunner.class)
@ContextConfiguration(
    classes = {
            Foo.class, ProgramsImpl.class
    }
)
public class FooTest {

  @SpyBean
  Programs programs;

  @Autowired
  Foo foo;

  @Before
  public void prepare() {
    when(programs.findAll())
            .thenReturn(
                    List.of(
                            "A", "B", "C"
                    )
            );
    this.foo.init();
  }

  @Test
  public void foo() {
    assertEquals(asList("A", "B", "C"), foo.getBlah());
  }
}

上面的测试按预期工作,也就是说,尽管ProgramsImpl.findAll返回"1", "2", "3"when子句包含的子句会用你自己的子句覆盖它。

但是,您可能会发现真的@PostConstruct被调用了两次:一次是由于,@Autowire第二次是由于init

如果你想避免它,你需要在你的测试中删除并创建你的内部@Autowire实例(使用“普通构造函数”)。例如:foo@Before

public class Foo {
  ...
  private Programs programs;

  @Autowired
  public Foo(Programs programs) {
    this.programs = programs;
  }
  ...
}


@RunWith(SpringRunner.class)
@ContextConfiguration(
    classes = {
            ProgramsImpl.class
    }
)
public class FooTest {

  @SpyBean
  Programs programs;

  Foo foo;

  @Before
  public void prepare() {
    when(programs.findAll())
            .thenReturn(
                    List.of(
                            "A", "B", "C"
                    )
            );
    this.foo = new Foo(programs);
    this.foo.init();
  }

  @Test
  public void foo() {
    assertEquals(asList("A", "B", "C"), foo.getBlah());
  }

 }

推荐阅读