Java 如何编写非封装单元测试?
我有一个自动连接的变量Java 如何编写非封装单元测试?,java,unit-testing,spring-boot,junit,Java,Unit Testing,Spring Boot,Junit,我有一个自动连接的变量 @Autowired private DocumentConfig documentConfig; 我想使用此配置对象的各种状态对DocumentService进行测试。我有什么选择?最好的选择是什么 第一个想法是: @Test public void save_failure() { documentConfig.setNameRequired(true); /* testing code goes here */ docume
@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,是否保证不会干扰其他测试?
我有什么办法可以说得更清楚些吗?无论是对我未来的自我还是对他人。目前还不清楚您使用的是哪种测试框架。对于普通单元测试,通过setter或构造函数注入使值可注入。什么最适合你的具体情况
如果不止三个;-对于要注入的这些值,您可以考虑引入一个配置类来将所有这些值作为单个参数注入。
目前还不清楚,您使用的测试框架是什么。对于普通单元测试,通过setter或构造函数注入使值可注入。什么最适合你的具体情况
如果不止三个;-对于要注入的这些值,您可以考虑引入一个配置类来将所有这些值作为单个参数注入。
可以在每次测试之前创建它。类Smth
private DocumentConfig documentConfig;
@Before
public void createConfig() {
documentConfig = new DocumentConfig(mockedParams);
}
您可以在每次测试之前创建它。类Smth
private DocumentConfig documentConfig;
@Before
public void createConfig() {
documentConfig = new DocumentConfig(mockedParams);
}
一种常用的方法是设置一个虚拟DocumentConfig,并将其注入到带有@Before注释的setUp方法中,以便在每个测试中重置整个上下文,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
在本例中,我设置了一个简单的对象,nameRequired为false。我可能会删除该语句,因为布尔字段默认为false
如果不使用构造函数注入,并且没有documentConfig的setter,则必须使用反射来注入字段,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
在测试中,您现在可以编写如下内容:
@Test
public void save_failure() {
this.documentConfig.setNameRequired(true);
// TODO: Implement test
}
@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
}
或者,您可以模拟DocumentConfig,这样您就不必依赖它的实现来测试DocumentService。我假设您在DocumentService代码中的某个地方调用isNameRequired,因此您可以这样模拟它:
@Test
public void save_failure() {
this.documentConfig.setNameRequired(true);
// TODO: Implement test
}
@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也有自己的runner,允许您摆脱设置方法,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
一种常用的方法是设置一个虚拟DocumentConfig,并将其注入到带有@Before注释的setUp方法中,以便在每个测试中重置整个上下文,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
在本例中,我设置了一个简单的对象,nameRequired为false。我可能会删除该语句,因为布尔字段默认为false
如果不使用构造函数注入,并且没有documentConfig的setter,则必须使用反射来注入字段,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
在测试中,您现在可以编写如下内容:
@Test
public void save_failure() {
this.documentConfig.setNameRequired(true);
// TODO: Implement test
}
@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
}
或者,您可以模拟DocumentConfig,这样您就不必依赖它的实现来测试DocumentService。我假设您在DocumentService代码中的某个地方调用isNameRequired,因此您可以这样模拟它:
@Test
public void save_failure() {
this.documentConfig.setNameRequired(true);
// TODO: Implement test
}
@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也有自己的runner,允许您摆脱设置方法,例如:
@Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
@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
}
}
你在测试什么?DocumentConfig,还是另一个依赖于DocumentConfig的类?如果是前者,请使用try/finally。如果是后者,则为mock DocumentConfig。您正在测试什么?DocumentConfig,还是另一个依赖于DocumentConfig的类?如果是前者,请使用try/finally。如果是后者,mock DocumentConfig.我似乎理解这个答案,我喜欢它。但“之后”会比“之前”好吗?毕竟,目标是始终保持配置重置,除了在特定测试中。有了这样的改变,这实际上与我自己的最终解决方案非常相似。编辑:然后我还需要在定义中分配变量,看起来。不管怎样,我不明白。这似乎没有将此文件中的documentConfig与DocumentService中的autowired documentConfig连接起来,DocumentService是这些测试和其他文件的主要对象。此外:我假设您指的是新的DocumentConfigmockedParams,对吗?我似乎理解这个答案,我喜欢它。但“之后”会比“之前”好吗?毕竟,目标是始终保持配置重置,除了在特定测试中。有了这样的变化,这实际上与我的
自己的最终解决方案。编辑:然后我还需要在定义中分配变量,看起来。不管怎样,我不明白。这似乎没有将此文件中的documentConfig与DocumentService中的autowired documentConfig连接起来,DocumentService是这些测试和其他文件的主要对象。此外:我假设您指的是新的DocumentConfigmockedParams,对吗?