代码之家  ›  专栏  ›  技术社区  ›  s-hunter

如何在RxJava 2中使用TestObserver对对象列表进行单元测试?

  •  2
  • s-hunter  · 技术社区  · 8 年前

    这是一个 人员类别 用于固定人和物体。它具有可比较的实现进行比较。

    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;
        }
    }
    

    这是一个 用于创建人员列表和人员列表可观察对象的工厂类 . 它总是创造同一组人。

    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,根据可观察的人员发出的人员列表测试人员列表 .

    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正在为一个普通的人员列表和一个可观察的人员列表创建相同的人员集,而Person类有一个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$1.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$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.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对对象列表进行单元测试的正确方法是什么?

    注意:以上所有代码都是在Android Studio中编写的。

    2 回复  |  直到 8 年前
        1
  •  3
  •   Murka    8 年前

    来自的javadoc Comparable :

    该接口对实现它的每个类的对象施加总顺序。。。 强烈建议但不严格要求(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 类,以便测试使用对象标识,并按预期失败。

        2
  •  1
  •   s-hunter    8 年前

    多亏了@Murka的回答,在我更换了 Comparable 实施 @Override public boolean equals(Object obj)

    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;
        }
    }