在我的类的某个单元测试中,我想断言已经为所有成员字段编写了断言。也就是说,给定
public class Foo {
int a;
int b;
String c;
}
我想做一些类似的事情
@Test
public void testFieldsAgainstExternalModel(Foo model, Bar externalModel) {
assertEquals(model.a, externalModel.getA());
assertEquals(model.b, externalModel.getSomethingNamedDifferently());
assertEquals(model.c, externalModel.getC().toString());
assertNumberOfAssertionsInThisTest(3); // <-- How do I do this?
}
当然,计算断言的数量并不能确保它们与每个字段都有断言有任何关系。但是,我只是想写一个测试,如果开发人员向Foo
添加了一个字段,却忘记更新这个测试,那么这个测试就会失败。
Naivest方法:我能想到的最简单的事情是使用反射来断言
Foo
具有的字段数,例如assertEquals(3, Foo.getDeclaredFields().count())
。但是,我可以很容易地看到程序员在不添加断言的情况下增加了字段数。这仍然是一种天真的方法,但可以接受:我认为,如果我至少能计数断言的数量,它将充分引导程序员达到预期的需求。
更准确的方法:当然,最好的方法似乎是实际保留一个表,其中哪些字段出现在断言中(例如,通过
assertEqualsWithCounters
这样的包装方法),但实际上我的情况有点复杂。例如,我的模型实际上是一个生成的类(@AutoMatter
),我将在断言的两侧使用getter,因此很难真正推断断言"接触"了哪个字段。这就是为什么对我来说,仅仅计算断言并保持对实际类型和计数的不可知性是最容易的
但是,我只是想写一个测试,如果开发人员添加字段,并忘记更新此测试。
我的公司使用映射框架将数据库中的bean(字段与数据库一一对应)映射到我们返回给客户端的模型bean,我们可以根据需要对其进行修改。我相信这是一个常见的用例,本质上也是您想要了解的。您应该不太关心测试对Foo
的更改,而应该更关心确保从Foo
到Bar
的映射不会中断
Class BeanDb{
private int a;
private int b;
...
}
Class Bean{
private int a;
private int b;
private String somethingElse;
...
}
然后我们有一个prodvder测试,它使用isEqualToComparingFieldByFieldRecursively
来比较这两个bean。
private Bean bean = new Bean();
private Bean beanDb = new BeanDb();
private BeanProvider provider = new BeanProvider();
@Before
public void setup(){
bean.setA(1);
bean.setB(2);
beanDb.setA(1);
beanDb.setB(2);
}
@Test
public void prepareForDatabase(){
BeanDb db = provider.prepareForDatabase(bean);
assertThat(beanDb).isEqualToComparingFieldByFieldRecursively(db);
}
@Test
public void populateFromDatabase(){
Bean model = provider.populateFromDatabase(beanDb);
assertThat(model).isEqualToComparingFieldByFieldRecursively(bean);
}
这实际上捕捉了很多错误。如果模型bean/dbbean中发生了变化,那么测试将在我们的连续集成套件中中断。这里需要注意的是isEqualToComparingFieldByFieldRecursively
不会捕获新添加的字段。当我查看代码/拉取请求并且BeanDb或提供商已经更新时,我会问:"你更新了提供商测试了吗?">
我认为您可能需要使用PowerMockito,因为Mockito不支持模拟静态方法。
我还没有测试下面的代码,但我的想法是做以下事情:
@RunWith(PowerMockRunner.class)
public class Tester {
public class Foo {
int a = 5;
int b = 6;
String c = "7";
}
class Bar {
public int getA() {
return 5;
}
public int getSomethingNamedDifferently() {
return 6;
}
public Integer getC() {
return 7;
}
}
@Test
public void testFieldsAgainstExternalModel() {
testFieldsAgainstExternalModel(new Foo(), new Bar());
}
public void testFieldsAgainstExternalModel(Foo model, Bar externalModel) {
Assert spiedAssert = PowerMockito.spy(Assert.class);
spiedAssert.assertEquals(model.a, externalModel.getA());
spiedAssert.assertEquals(model.b, externalModel.getSomethingNamedDifferently());
spiedAssert.assertEquals(model.c, externalModel.getC().toString());
PowerMockito.verify(spiedAssert, times(3)).assertEquals(any(), any());
}
}
我认为的答案
assertNumberOfAssertionsInThisTest(3); // <-- How do I do this?
是:你做而不是那样做。这个测试几乎没用。
如果只有3个字段,您可以用眼睛看到您正在检查这三个字段。您可能正在考虑该类中的15个字段,但是:
assertEquals(model.a, externalModel.getA());
assertEquals(model.b, externalModel.getSomethingNamedDifferently());
assertEquals(model.c, externalModel.getC().toString());
assertEquals(model.c, externalModel.getC().toString());
assertEquals(model.d, externalModel.getD().toString());
assertEquals(model.o,externalModel.getO().toString());
assertNumberOfAssertionsInThisTest(15);
不会帮助您。你猜怎么着:当我把伪造的例子放在一起时,我省略了属性14(model.n),但我出现了复制/粘贴错误,并检查了model.c两次,结果总数为15。
因此,我同意用户Phil的观点,只是建议了另一种方法,比如:
- 确保bean类具有合理的
equals()
(可能还有hashCode()
方法)。通过使用Lombok,或者通过拥有一个使用EqualsBuilder的基类(我们实际上在稍后的项目中这样做) - 然后让测试代码使用一个fakedbean,并简单地将该fakedbean是否等于与您的生产代码从(可能是faked)外部模型对象创建的bean进行比较