如何编写非封装的单元测试?

时间:2019-04-05 10:13:20

标签: java unit-testing spring-boot junit

我有一个自动连接的变量

@Autowired
private DocumentConfig documentConfig;

我想使用此配置对象的各种状态对DocumentService进行测试。我有什么选择?最好的选择是什么?

第一个想法是:

@Test
public void save_failure() {
    documentConfig.setNameRequired(true);
    /*
    testing code goes here
    */
    documentConfig.setNameRequired(false);
}

但是我想更加确定在测试之后将变量重置为不干扰其他测试,以确保只有此测试才是导致问题的原因。

我的新主意是:

@Before
public void after() { documentConfig.setNameRequired(true); }
@Test
public void save_failure() {
    /*
    testing code goes here
    */
}
@After
public void after() { documentConfig.setNameRequired(false); }

但是,这根本不起作用,因为对整个文件而不是对单个测试执行“前后”操作。我不希望只为一个测试就创建一个新文件。

我现在已经达成妥协:

@Test
public void save_failure() {
    documentConfig.setNameRequired(true);
    /*
    testing code goes here
    */
}
@After
public void after() { documentConfig.setNameRequired(false); }

似乎可以做我想做的所有事情,但是我有几个问题。
假设nameRequired以false开头,是否保证不会干扰其他测试?
有什么办法可以使我更清楚吗?为了我自己和他人。

3 个答案:

答案 0 :(得分:2)

您可以在每次测试之前创建它。有点像

private DocumentConfig documentConfig;

@Before
public void createConfig() {
    documentConfig = new DocumentConfig(mockedParams);
}

答案 1 :(得分:1)

使用哪种测试框架尚不清楚。对于普通单元测试,请通过设置器或构造函数注入使该值可注入。无论哪种情况最适合您的具体情况。

如果要注入很多(超过三个;-),则可以考虑引入配置类以将所有这些值作为单个参数注入。

答案 2 :(得分:1)

一种常用的方法是设置一个虚拟DocumentConfig并将其注入setUp()方法(用@Before注释)中,以便在每个测试中重置整个上下文,例如例如:

@Before
public void setUp() {
    this.documentConfig = new DocumentConfig();
    this.documentConfig.setNameRequired(false);
    this.service = new DocumentService(this.documentConfig);
}

在这种情况下,我设置了一个简单的对象,其中nameRequiredfalse。我可能可以删除该语句,因为无论如何boolean字段默认为false

如果不使用构造函数注入,并且没有documentConfig的二传手,则必须使用反射来注入字段,例如:

ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);

在测试中,您现在可以编写如下内容:

@Test
public void save_failure() {
    this.documentConfig.setNameRequired(true);
    // TODO: Implement test
}

或者,您可以模拟DocumentConfig,这样就不必依赖其实现来测试DocumentService。我假设您正在isNameRequired()的代码中的某个地方调用DocumentService,因此可以这样模拟它:

@Before
public void setUp() {
    // Use a static import for Mockito.mock()
    this.documentConfig = mock(DocumentConfig.class);
    this.service = new DocumentService(this.documentConfig);
}

@Test
public void save_failure()  {
    // Use a static import for Mockito.when()
    when(this.documentConfig.isNameRequired()).thenReturn(true); 
    // TODO: Implement test
}

由于这种模拟/注入设置经常发生,因此Mockito也拥有自己的运行程序,可让您摆脱setUp()方法,例如:

@RunWith(MockitoJUnitRunner.class)
public class DocumentServiceTest {
    @InjectMocks
    private DocumentService documentService;
    @Mock
    private DocumentConfig documentConfig;

    @Test
    public void save_failure()  {
        when(this.documentConfig.isNameRequired()).thenReturn(true); 
        // TODO: Implement test
    }
}