我有一个具有相当复杂的静态初始化的类。我正在从目录中读取文件,然后解析这些 json 文件,映射到对象,并填写一个列表。你可以想象,可能会出现一些异常,我需要覆盖并测试这些代码分支。问题是这个静态初始化只运行一次/Testcase 文件。我遇到的解决方案:
- 每个行为的新测试用例文件
- 卸载静态类
- 新的JVM
我对这些选项不感兴趣,难道没有更好的选择吗?
我有一个具有相当复杂的静态初始化的类。我正在从目录中读取文件,然后解析这些 json 文件,映射到对象,并填写一个列表。你可以想象,可能会出现一些异常,我需要覆盖并测试这些代码分支。问题是这个静态初始化只运行一次/Testcase 文件。我遇到的解决方案:
我对这些选项不感兴趣,难道没有更好的选择吗?
如果你不能避免静态初始化程序将它的主体提取到方法中。然后测试方法。您的静态初始化程序看起来static { myMethod(); }
很难被破坏。
单元测试中的一个重要因素是构建代码以使其适合测试。
不幸的是,正如您所发现的,执行复杂 IO 操作的具有过多静态初始化的代码并不是一个易于测试的结构。
除了静态初始化之外,听起来你的代码违反了单一职责原则,就像一个类从外部源加载自身并且可能有其他用途一样。
因此,您需要进行一些重构,例如,如果您的代码看起来像这样(JSON 解析,为清晰起见替换为 CSV 解析):
public MyClass
{
private static List<MyObject> myObjects = new ArrayList<>();
static
{
try
{
try (BufferedReader reader = new BufferedReader(new FileReader(myfile.csv))
{
String line;
while ((line = reader.readLine()) != null)
{
String[] tokens = line.split(",");
myObjects.add(new MyObject(tokens[0], tokens[1], tokens[2]));
}
}
}
catch (IndexOutOfBoundsException e)
{
...
}
catch (IOException e)
{
...
}
}
}
然后您可以将大部分逻辑提取到自定义阅读器类中,如下所示:
public class MyObjectReader implements Closeable
{
private BufferredReader reader;
public MyObjectReader(Reader reader)
{
this.reader = new BufferredReader(reader);
}
public MyObject read() throws IOException
{
String line = reader.readLine();
if (line != null)
{
String[] tokens = line.split(",");
if (tokens.length < 3)
{
throw new IOException("Invalid line encountered: " + line);
}
return new MyObject(tokens[0], tokens[1], tokens[2]);
}
else
{
return null;
}
}
public void close() throws IOException
{
this.reader.close();
}
}
该类MyObjectReader
是完全可测试的,重要的是不依赖于文件或其他资源,因此您可以像这样测试它:
public MyObjectReaderTest
{
@Test
public void testRead() throws IOException
{
String input = "value1.1,value1.2,value1.3\n" +
"value2.1,value2.2,value2.3\n" +
"value3.1,value3.2,value3.3";
try (MyObjectReader reader = new MyObjectReader(new StringReader(input)))
{
assertEquals(new MyObject("value1.1", "value1.2", "value1.3"), reader.read());
assertEquals(new MyObject("value2.1", "value2.2", "value2.3"), reader.read());
assertEquals(new MyObject("value3.1", "value3.2", "value3.3"), reader.read());
assertNull(reader.read());
}
}
@Test(expected=IOException.class)
public void testReadWithInvalidLine() throws IOException
{
String input = "value1.1,value1.2";
try (MyObjectReader reader = new MyObjectReader(new StringReader(input)))
{
reader.read();
}
}
}
在没有看到您的代码或不知道文件格式的情况下,很难对此进行扩展,但希望您能掌握要点。
最后,您的静态初始化将只是:
public MyClass
{
private static List<MyObject> myObjects = new ArrayList<>();
static
{
loadMyObjects(new FileReader("myfile.csv"));
}
/* package */ static void loadMyObjects(Reader reader)
{
try
{
try (MyObjectReader reader = new new MyObjectReader(reader))
{
MyObject myObject;
while ((myObject = reader.read()) != null)
{
myObjects.add(myObject);
}
}
}
catch (IOException e)
{
...
}
}
}
可能值得在这里测试快乐的道路,但就个人而言,该loadMyObjects
方法现在非常简单,我可能不会打扰。