如何在 RxJava 2 中使用 TestObserver 对对象列表进行单元测试?
How to unit test a list of objects using TestObserver in RxJava 2?
这里有一个 Person class 用来存放人物对象。它具有 Comparable 实现以供比较。
public class Person implements Comparable<Person> {
private String firstName = "";
private String lastName = "";
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int compareTo(@NonNull Person person) {
return this.firstName.compareTo(person.getFirstName());
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
这是一个 工厂 class,用于创建人员列表和人员列表 Observables。它总是创建相同的一组人。
import java.util.ArrayList;
import io.reactivex.Observable;
public class PeopleFactory {
public static ArrayList<Person> createPeople() {
ArrayList<Person> people = new ArrayList<>();
for (int i=0; i<10; i++) {
people.add(new Person("Thanks" + i, "Giving" + i));
}
return people;
}
public static Observable<ArrayList<Person>> createPersonObservable() {
return Observable.just(createPeople());
}
}
现在我想使用 RxJava 2 中的 TestObserver 来根据 people Observable.
发出的人员列表来测试人员列表
import org.junit.Test;
import java.util.ArrayList;
import io.reactivex.Observable;
import io.reactivex.observers.TestObserver;
public class PersonTest {
@Test
public void testPeople() {
// Creating a people Observable
Observable<ArrayList<Person>> peopleObservable = PeopleFactory.createPersonObservable();
// Creating an test observer and subscribe to the above peopleObservable
TestObserver<ArrayList<Person>> observer = new TestObserver<>();
peopleObservable.subscribe(observer);
// Creating the same people list and assert this people list with the people list from the peopleObservable
ArrayList<Person> people = PeopleFactory.createPeople();
observer.assertValue(people);
}
}
我期待这个测试应该通过,因为 PeopleFactory 正在为普通人员列表和人员列表 Observable 创建同一组人员,并且 Person class 有一个 Comparable,表示对象将是只要 firstName 相等就相等。
但是 测试忽略了这个 Comparable,将它与对象地址进行比较,并给了我以下测试失败消息:
java.lang.AssertionError: Expected: [com.example.myapplication.Person@7006c658, com.example.myapplication.Person@34033bd0, com.example.myapplication.Person@47fd17e3, com.example.myapplication.Person@7cdbc5d3, com.example.myapplication.Person@3aa9e816, com.example.myapplication.Person@17d99928, com.example.myapplication.Person@3834d63f, com.example.myapplication.Person@1ae369b7, com.example.myapplication.Person@6fffcba5, com.example.myapplication.Person@34340fab] (class: ArrayList), Actual: [com.example.myapplication.Person@2aafb23c, com.example.myapplication.Person@2b80d80f, com.example.myapplication.Person@3ab39c39, com.example.myapplication.Person@2eee9593, com.example.myapplication.Person@7907ec20, com.example.myapplication.Person@546a03af, com.example.myapplication.Person@721e0f4f, com.example.myapplication.Person@28864e92, com.example.myapplication.Person@6ea6d14e, com.example.myapplication.Person@6ad5c04e] (class: ArrayList) (latch = 0, values = 1, errors = 0, completions = 1)
at io.reactivex.observers.BaseTestConsumer.fail(BaseTestConsumer.java:163)
at io.reactivex.observers.BaseTestConsumer.assertValue(BaseTestConsumer.java:332)
at com.example.myapplication.PersonTest.testPeople(PersonTest.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
那么,什么是正确的方法或者我如何在 RxJava 2 中使用 TestObserver 对对象列表进行单元测试?
注:以上所有代码均在AndroidStudio中编写。
来自 Comparable
的 javadoc:
This interface imposes a total ordering on the objects of each class that implements it. ...
It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)).
Comparable::compareTo
方法仅在排序或比较元素顺序时使用。平等是完全独立的,正如您所指出的,正在断言对象身份。
你可以通过查看源码快速确认框架在做什么https://github.com/ReactiveX/RxJava/blob/v2.1.6/src/main/java/io/reactivex/observers/BaseTestConsumer.java#L331
if (!ObjectHelper.equals(value, v)) {
throw fail("Expected: " + valueAndClass(value) + ", Actual: " + valueAndClass(v));
}
ObjectHelper::equals
public static boolean equals(Object o1, Object o2) { // NOPMD
return o1 == o2 || (o1 != null && o1.equals(o2));
}
所以它正在调用 Object::equals
,您没有为 Person
class 超载,所以测试使用对象标识并按预期失败。
感谢@Murka 的回答,在我用 @Override public boolean equals(Object obj)
替换 Comparable
实现后,它按预期工作
public class Person {
private String firstName = "";
private String lastName = "";
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public boolean equals(Object obj) {
return this.firstName.equalsIgnoreCase(((Person) obj).getFirstName());
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
这里有一个 Person class 用来存放人物对象。它具有 Comparable 实现以供比较。
public class Person implements Comparable<Person> {
private String firstName = "";
private String lastName = "";
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int compareTo(@NonNull Person person) {
return this.firstName.compareTo(person.getFirstName());
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
这是一个 工厂 class,用于创建人员列表和人员列表 Observables。它总是创建相同的一组人。
import java.util.ArrayList;
import io.reactivex.Observable;
public class PeopleFactory {
public static ArrayList<Person> createPeople() {
ArrayList<Person> people = new ArrayList<>();
for (int i=0; i<10; i++) {
people.add(new Person("Thanks" + i, "Giving" + i));
}
return people;
}
public static Observable<ArrayList<Person>> createPersonObservable() {
return Observable.just(createPeople());
}
}
现在我想使用 RxJava 2 中的 TestObserver 来根据 people Observable.
发出的人员列表来测试人员列表import org.junit.Test;
import java.util.ArrayList;
import io.reactivex.Observable;
import io.reactivex.observers.TestObserver;
public class PersonTest {
@Test
public void testPeople() {
// Creating a people Observable
Observable<ArrayList<Person>> peopleObservable = PeopleFactory.createPersonObservable();
// Creating an test observer and subscribe to the above peopleObservable
TestObserver<ArrayList<Person>> observer = new TestObserver<>();
peopleObservable.subscribe(observer);
// Creating the same people list and assert this people list with the people list from the peopleObservable
ArrayList<Person> people = PeopleFactory.createPeople();
observer.assertValue(people);
}
}
我期待这个测试应该通过,因为 PeopleFactory 正在为普通人员列表和人员列表 Observable 创建同一组人员,并且 Person class 有一个 Comparable,表示对象将是只要 firstName 相等就相等。
但是 测试忽略了这个 Comparable,将它与对象地址进行比较,并给了我以下测试失败消息:
java.lang.AssertionError: Expected: [com.example.myapplication.Person@7006c658, com.example.myapplication.Person@34033bd0, com.example.myapplication.Person@47fd17e3, com.example.myapplication.Person@7cdbc5d3, com.example.myapplication.Person@3aa9e816, com.example.myapplication.Person@17d99928, com.example.myapplication.Person@3834d63f, com.example.myapplication.Person@1ae369b7, com.example.myapplication.Person@6fffcba5, com.example.myapplication.Person@34340fab] (class: ArrayList), Actual: [com.example.myapplication.Person@2aafb23c, com.example.myapplication.Person@2b80d80f, com.example.myapplication.Person@3ab39c39, com.example.myapplication.Person@2eee9593, com.example.myapplication.Person@7907ec20, com.example.myapplication.Person@546a03af, com.example.myapplication.Person@721e0f4f, com.example.myapplication.Person@28864e92, com.example.myapplication.Person@6ea6d14e, com.example.myapplication.Person@6ad5c04e] (class: ArrayList) (latch = 0, values = 1, errors = 0, completions = 1)
at io.reactivex.observers.BaseTestConsumer.fail(BaseTestConsumer.java:163)
at io.reactivex.observers.BaseTestConsumer.assertValue(BaseTestConsumer.java:332)
at com.example.myapplication.PersonTest.testPeople(PersonTest.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
那么,什么是正确的方法或者我如何在 RxJava 2 中使用 TestObserver 对对象列表进行单元测试?
注:以上所有代码均在AndroidStudio中编写。
来自 Comparable
的 javadoc:
This interface imposes a total ordering on the objects of each class that implements it. ... It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)).
Comparable::compareTo
方法仅在排序或比较元素顺序时使用。平等是完全独立的,正如您所指出的,正在断言对象身份。
你可以通过查看源码快速确认框架在做什么https://github.com/ReactiveX/RxJava/blob/v2.1.6/src/main/java/io/reactivex/observers/BaseTestConsumer.java#L331
if (!ObjectHelper.equals(value, v)) {
throw fail("Expected: " + valueAndClass(value) + ", Actual: " + valueAndClass(v));
}
ObjectHelper::equals
public static boolean equals(Object o1, Object o2) { // NOPMD
return o1 == o2 || (o1 != null && o1.equals(o2));
}
所以它正在调用 Object::equals
,您没有为 Person
class 超载,所以测试使用对象标识并按预期失败。
感谢@Murka 的回答,在我用 @Override public boolean equals(Object obj)
Comparable
实现后,它按预期工作
public class Person {
private String firstName = "";
private String lastName = "";
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public boolean equals(Object obj) {
return this.firstName.equalsIgnoreCase(((Person) obj).getFirstName());
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}