测试从文件中读取和处理数据的方法
Testing a method that reads and processes data from a file
我有以下代码:
public class FolderServiceImpl implements FolderService {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
if (folderStatusFile.exists()) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(folderStatusFile));
String line = br.readLine();
status = Integer.parseInt(line);
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
L.warn("could not close reader ", e);
}
}
}
} else {
status = 3;
}
return status;
}
}
我想在不为每个案例创建实际文件的情况下测试此方法。我应该使用 Java 1.7、JUnit 4、Mockito and/or PowerMockito。
关于如何做到这一点有什么想法吗?
我说的是模拟数据源或简单地更改方法的输入。
我的测试看起来像这样:
`@规则
public TemporaryFolder 文件夹 = new TemporaryFolder();
private FolderServiceImpl serviceToTest = new FolderServiceImpl();
private String folderPath;
@Before
public void setUp() {
folderPath = folder.getRoot().getAbsolutePath();
try {
folder.newFile(".folderstatus");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void shouldReturnFolderStatus3WhenFolderStatusIsNotFound() {
// given
deleteFolderStatusFile();
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(3, status);
}
@Test
public void shouldReturnFolderStatus4WhenTheStatusIsUnreadable() {
// given
writeStatusToTestFile("Test");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(4, status);
}
@Test
public void shouldReturnFolderStatusInTheFile() {
// given
writeStatusToTestFile("1");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(1, status);
}
private void writeStatusToTestFile(String status) {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.write(file, status.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private void deleteFolderStatusFile() {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.delete(file);
} catch (IOException e) {
e.printStackTrace();
}
}`
你必须使用这样的东西:
@RunWith(PowerMockRunner.class)
@PrepareForTest(tests.class)
public class test {
@Test
public void test() throws Exception {
File fileMock = Mockito.mock(File.class);
PowerMockito.whenNew(File.class).withArguments(Mockito.anyString(), Mockito.anyString()).thenReturn(fileMock);
FolderServiceImpl sut = new FolderServiceImpl sut ();
Mockito.when(fileMock.exists()).thenReturn(true);
sut.getStatus("");
// Your verifications..
}
}
Powermock 将模拟在 class 的 getStatus 方法中创建的文件对象。使用 Mockito.when,您可以在代码中说明 folderStatusFile.exists() 的 return 值是多少。
编辑
我在maven中包含了以下两个jar,但你不需要使用maven:https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4/1.4.6 and https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito/1.4.9 and https://mvnrepository.com/artifact/org.mockito/mockito-all/1.10.19
虽然@BenHeid 的回答可能有效,但我建议改用不同的方法。
恕我直言,每当我使用 PowerMock(-ito) 时,它都是对不良设计的投降。
此外,PowerMock 解决方案会混淆测试覆盖率工具,因为它会在应用程序字节码被用于覆盖率测量后更改它。
所以我更喜欢的方法是坚持清洁代码 和 OOP 规则。
其中之一是关注点分离。
在您的情况下,该方法创建了一些基础设施 classes(依赖项)以供使用,即 FileReader
和 BufferedReader
.
但是(直接)依赖项的实例化不是 class 包含业务逻辑的责任。
因此我建议将该代码重构为单独的 class:
class ReaderFactory {
public BufferedReader createFor(File file) throws FileNotFoundException {
return new BufferedReader(new FileReader(file));
}
}
您的 Class 将更改为:
class FolderServiceImpl {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
private final ReaderFactory readerFactory;
FolderServiceImpl(ReaderFactory readerFactory) {
this.readerFactory = readerFactory;
}
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
// try "with resource" takes care of closing the reader
try (BufferedReader br = readerFactory.createFor(folderStatusFile);) {
String line = br.readLine();
status = Integer.parseInt(line);
} catch (IOException e) {
status = 3;
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
}
return status;
}
}
你的测试是这样的:
public class FolderServiceImplTest {
private static final String ANY_FILE_NAME = "";
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Mock
private ReaderFactory readerFactory;
@InjectMocks
FolderServiceImpl sut;
@Test
public void getStatus_FileNotExisting_returnStatus3() throws Exception {
// arrange
Mockito.doThrow(new FileNotFoundException("UnitTest")).when(readerFactory).createFor(Mockito.any(File.class));
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(3));
}
@Test
public void getStatus_ValidFile_returnFileContentAsInt() throws Exception {
// arrange
BufferedReader bufferedReader = Mockito.mock(BufferedReader.class);
Mockito.doReturn(bufferedReader).when(readerFactory).createFor(Mockito.any(File.class));
Mockito.doReturn("4711").when(bufferedReader).readLine();
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(4711));
}
}
我有以下代码:
public class FolderServiceImpl implements FolderService {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
if (folderStatusFile.exists()) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(folderStatusFile));
String line = br.readLine();
status = Integer.parseInt(line);
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
L.warn("could not close reader ", e);
}
}
}
} else {
status = 3;
}
return status;
}
}
我想在不为每个案例创建实际文件的情况下测试此方法。我应该使用 Java 1.7、JUnit 4、Mockito and/or PowerMockito。
关于如何做到这一点有什么想法吗?
我说的是模拟数据源或简单地更改方法的输入。
我的测试看起来像这样:
`@规则 public TemporaryFolder 文件夹 = new TemporaryFolder();
private FolderServiceImpl serviceToTest = new FolderServiceImpl();
private String folderPath;
@Before
public void setUp() {
folderPath = folder.getRoot().getAbsolutePath();
try {
folder.newFile(".folderstatus");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void shouldReturnFolderStatus3WhenFolderStatusIsNotFound() {
// given
deleteFolderStatusFile();
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(3, status);
}
@Test
public void shouldReturnFolderStatus4WhenTheStatusIsUnreadable() {
// given
writeStatusToTestFile("Test");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(4, status);
}
@Test
public void shouldReturnFolderStatusInTheFile() {
// given
writeStatusToTestFile("1");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(1, status);
}
private void writeStatusToTestFile(String status) {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.write(file, status.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private void deleteFolderStatusFile() {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.delete(file);
} catch (IOException e) {
e.printStackTrace();
}
}`
你必须使用这样的东西:
@RunWith(PowerMockRunner.class)
@PrepareForTest(tests.class)
public class test {
@Test
public void test() throws Exception {
File fileMock = Mockito.mock(File.class);
PowerMockito.whenNew(File.class).withArguments(Mockito.anyString(), Mockito.anyString()).thenReturn(fileMock);
FolderServiceImpl sut = new FolderServiceImpl sut ();
Mockito.when(fileMock.exists()).thenReturn(true);
sut.getStatus("");
// Your verifications..
}
}
Powermock 将模拟在 class 的 getStatus 方法中创建的文件对象。使用 Mockito.when,您可以在代码中说明 folderStatusFile.exists() 的 return 值是多少。
编辑
我在maven中包含了以下两个jar,但你不需要使用maven:https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4/1.4.6 and https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito/1.4.9 and https://mvnrepository.com/artifact/org.mockito/mockito-all/1.10.19
虽然@BenHeid 的回答可能有效,但我建议改用不同的方法。
恕我直言,每当我使用 PowerMock(-ito) 时,它都是对不良设计的投降。 此外,PowerMock 解决方案会混淆测试覆盖率工具,因为它会在应用程序字节码被用于覆盖率测量后更改它。
所以我更喜欢的方法是坚持清洁代码 和 OOP 规则。
其中之一是关注点分离。
在您的情况下,该方法创建了一些基础设施 classes(依赖项)以供使用,即 FileReader
和 BufferedReader
.
但是(直接)依赖项的实例化不是 class 包含业务逻辑的责任。
因此我建议将该代码重构为单独的 class:
class ReaderFactory {
public BufferedReader createFor(File file) throws FileNotFoundException {
return new BufferedReader(new FileReader(file));
}
}
您的 Class 将更改为:
class FolderServiceImpl {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
private final ReaderFactory readerFactory;
FolderServiceImpl(ReaderFactory readerFactory) {
this.readerFactory = readerFactory;
}
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
// try "with resource" takes care of closing the reader
try (BufferedReader br = readerFactory.createFor(folderStatusFile);) {
String line = br.readLine();
status = Integer.parseInt(line);
} catch (IOException e) {
status = 3;
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
}
return status;
}
}
你的测试是这样的:
public class FolderServiceImplTest {
private static final String ANY_FILE_NAME = "";
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Mock
private ReaderFactory readerFactory;
@InjectMocks
FolderServiceImpl sut;
@Test
public void getStatus_FileNotExisting_returnStatus3() throws Exception {
// arrange
Mockito.doThrow(new FileNotFoundException("UnitTest")).when(readerFactory).createFor(Mockito.any(File.class));
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(3));
}
@Test
public void getStatus_ValidFile_returnFileContentAsInt() throws Exception {
// arrange
BufferedReader bufferedReader = Mockito.mock(BufferedReader.class);
Mockito.doReturn(bufferedReader).when(readerFactory).createFor(Mockito.any(File.class));
Mockito.doReturn("4711").when(bufferedReader).readLine();
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(4711));
}
}