我有一个课程,其中没有详细介绍两个方法
validateData(Person[] p);
processData(Person[] p);
通常这些将被用作:
if(validateData(myData)) {
processData(myData);
}
使用JUnit并为上述两种方法创建测试用例的最佳方法是什么?我不确定@Before
在这种情况下是否正确使用。我希望每个@Test
调用validateData(myData)
,然后如果它完成且没有错误并且返回true,那么测试应该使用相同的processData(myData)
调用myData
但是将编程逻辑引入到我认为不好的测试用例中。
答案 0 :(得分:0)
由于这两种方法都是公开的,因此每种方法都应该有各自的单元测试。假设情况如此,您可以在Person []上查看一些内容,以确保 processData 做了一些事情。
你可以做的其他事情是重构代码以使用对象而不仅仅是方法 - 然后你可以模拟出对象,并确保根据特定条件调用它们:
public interface IDataProcessor {
void processData(Data data);
}
public class Foo{
public Foo(IDataProcessor processor) {
_processor = processor;
}
public void bar(Data data){
if(validateData(data)){
_processor.processData(data);
}
}
private boolean validateData(Data data){
return data != null; // or whatever you want
}
private IDataProcessor _processor;
}
public class MockProcessor implements IDataProcessor {
public MockProcessor(){}
public void processData(Data data){
CallFlag = true;
}
public bool CallFlag = false;
}
然后,对于您的单元测试,您可以这样做:
MockProcessor mock = new MockProcessor();
Foo foo = new Foo(mock);
Data data = new Data();
foo.bar(data);
Assert.True(mock.CallFlag);
在C#中有一个名为Moq的优秀库,这使得在单元测试中创建接口的模拟实现非常容易,而无需自己手动定义它们。 Java中可能存在类似的东西。
答案 1 :(得分:0)
您在评论中提到您担心测试中的冗余。这通常是一个好主意,但您应该理解“生产”和“测试”代码之间存在细微差别。
你看,测试代码的主要意图是
a)快速指出失败
b)让您轻松修复它们。
换句话说:如果你所测试的课程有3个公共方法,那么你的测试类也至少有3个测试方法。但最有可能的是,您的测试类将拥有测试方法,以及您可以通过生产方法获取的每个路径。
是的,这导致了很多测试方法。但重点是:如果其中一个失败,可能只需要30秒就可以确定发生了什么 - 相比之下:当你只有一个测试方法时,整个过程;你将不得不花费相当长的时间来了解你太长时间测试的哪一部分实际上是失败的。但当然:需要在“我的测试易于理解”和“我在各处复制代码”之间取得平衡。这个podcast为进一步思考这个主题提供了一些很好的起点。
最后,从SOLID的角度来看,也考虑拆分您的生产代码。类应该只对一件事负责 - 并且单独的方法名称表明你的类似乎负责做多个事情(显然:验证和处理)。请注意,使用这两种方法也会使您的界面不清楚 - 该类的用户是否希望在处理之前调用validate;或者“处理”包括验证也发生了吗?