How to test arguments passed to constructor on a factory method?
我遇到了一个我真不知道如何解决的情况。我刚刚创建了一个类Foo,它包含一个静态工厂方法,使用一些参数创建类Bar,因此Foo是一种工厂类。现在我想测试这个方法,但问题是,这些参数在内部被修改,以创建在Bar上调用构造函数所需的变量。作为测试的一部分,我可以测试我得到Bar的最终对象,但我不知道如何验证传递给构造函数的参数。
1 2 3 4 5 6
| public class Foo {
public Bar createBarOf (String argumentOne, String argumentTwo ) {
String argumentForBar = argumentOne + argumentTwo ;
return new Bar (argumentForBar );
}
} |
有什么建议?
编辑
为了使问题更清楚,这是Bar类。
1 2 3 4 5 6 7 8
| public class Bar {
private final String SUFFIX ="BarSuffix";
private String field ;
public Bar (String argumentForBar ) {
field = argumentForBar + BAR_SUFFIX ;
}
} |
所以,考虑到这个构造函数。当我在Foo中测试createBarFor(argumentOne,argumentTwo)时,我无法弄清楚如何测试那个argumentOne和argumentTwo用于创建Bar,除非我还在该测试中测试Bar构造函数并假设Bar的内部是什么。 argumentOne和argumentTwo不是Bar的存储字段,而是变量来计算对象中字段的值。
只需使用createBarOf并对返回的Bar对象断言您的期望。 当你使用参数a和b创建ab时,你不应该试图断言ab被传递给构造函数,当你获得Bar对象时,它已经被包含了。
由于createBarOf内没有退出逻辑(从方法返回的执行分支),因此您不必专门测试有关此方法的任何内容。
-
但是我不应该在Bar类的特定单元测试中测试我的期望吗?测试栏作为Foo测试的一部分,将为Bar(至少对于它的构造函数)进行冗余单元测试。我可以在这里测试工厂方法和Bar构造函数,但Bar的单元测试不包含其构造函数的测试似乎很奇怪。而只是测试我从工厂方法得到一个Bar类型的对象不能确保为构造函数创建参数的逻辑(实际代码比concat两个字符串更复杂)
-
@Kilian:我并不是说你测试你收到了一个Bar对象。我的意思是你在返回的Bar对象上执行断言;这个论点有什么影响?它改变了一些变量吗?然后在Bar.getArgument()上断言。您的单元测试可以包含多个分层单元:请查看我的答案stackoverflow.com/a/17731324/1864167。
-
我遇到的问题是传递的参数永远不会存储在Bar对象中。它在构造函数中用于填充其他对象的列表。有我的问题,没有Bar.getArgumentForBar()但Bar.getNewCalculatedValue()。如果我想验证Foo工厂方法使用传递的参数,除非有单一测试中捕获这些参数的方法,我还必须测试Bar中的构造函数逻辑,我想在单元测试中做具体的。
-
你正在寻找一个太直接的结果,我无法让它更清晰,因为你的问题太抽象了。当你传入"a"和"b"时,这对你的Bar构造有影响,对吧?使用断言验证此最终效果。我建议你也看看我的博客,我稍微详细说明了这一点(见该帖的结尾)。
-
我编辑了我的注释,以显示如何在Bar构造函数中使用参数。在该代码中,没有办法验证argumentOne和argumentTwo,因为它们没有存储在任何地方,而是用于使用常量来计算"字段"。这个例子是我的问题的简化版本,以显示除非测试还测试Bar构造函数,否则传递的参数是如何解决的。
-
是的,但你仍然没有展示它是如何使用的。只需测试最终使用field的代码,看看是否完美无缺。它含蓄地测试了这一点。
-
@Jeroen是对的。最终,私有字段field必须在类中有某种用途,即使没有明确的getter。在设置之后断言field的用法。
-
好的,所以你要说的是,测试工厂方法的唯一方法是验证field包含构造函数中的逻辑应用后的预期数据。我问这个的原因是因为在我看来,如何使用field或它包含什么并不重要。测试应该只验证构造函数的参数的逻辑,而不是构造函数本身。这应该是Bar的单元测试的一部分,而不是Foo的单元测试。但是如果没有办法做到这一点,那么我应该得到field并断言它以便构造函数也在这里进行测试。
-
不,我们明确表示您不应该特别测试field。我们说你应该只使用Bar实例并测试它。 field值将在某处使用,最终将通过您可以测试的公共方法进行隐式测试。如果您能够显示如何使用它的明确示例,这将更容易解释;)
-
对不起,但我不能把真实的例子因为很大。在那个,在工厂方法中计算的参数在构造函数中使用,以在其中创建另一个复杂对象。这就是为什么对我来说测试方法的使用是没有意义的,因为它依赖于其他几个方法。这些其他的有自己的单元测试,因此在工厂方法中测试使用它们将是多余的。我期望的行为会使测试变得复杂,这应该是相当简单的。我结束了访问里面的对象并验证我期望的值
我有类似的情况。 如果您的Bar有一个equals方法,您可以随时构建您希望工厂在测试中构建的Bar ...
1
| assertThat(actualbar).isEqualTo(expectedBar) |
但是如果没有任何意义可以拥有"等于",或者你不能因为某种原因改变"Bar",那么你可能没有太多选择,只能通过反射进入Bar的私人领域,看看是否 设置正确的值。
例如(这使用名为ReflectionTestUtils的"spring-test"类)...
1
| assertThat(ReflectionTestUtils.getField(actualBar,"argumentForBar")).isEqualTo(expectedValue); |
-
正如之前的回复所评论的那样,在Bar的特定单元测试中测试构造函数更有意义,而不是将其作为Foo中工厂方法测试的一部分。对我来说,这里应该测试的是为Bar构造函数创建最终参数的逻辑,而不是构造函数的工作原理(应该是另一个测试)。但是如果没有其他方法,我想我将不得不在这里调用构造函数来测试结果。