googletest icon indicating copy to clipboard operation
googletest copied to clipboard

Deep comparison between objects

Open Denis-Gavrielov opened this issue 3 years ago • 4 comments

I was interested to see if there are solutions to compare deeply nested structs in C++ where differences in the structs are clearly highlighted on failure.

This is something that Jest in Javascript seems to be doing really well. For example:

    const one = {
      foo: "bar",
      first: {
        second: {
          third: ["a", "b", "c"]
        },
        anotherOne: 42
      }
    }
    
    const two = {
      foo: "bar",
      first: {
        second: {
          third: ["a", "b", "d"]
        },
        anotherOne: 43
      }
    }

    expect(one).toEqual(two);

Will show the following output:

    expect(received).toEqual(expected) // deep equality

    - Expected  - 2
    + Received  + 2

      Object {
        "first": Object {
    -     "anotherOne": 43,
    +     "anotherOne": 42,
          "second": Object {
            "third": Array [
              "a",
              "b",
    -         "d",
    +         "c",
            ],
          },
        },
        "foo": "bar",
      }

      36 |     }
      37 |
    > 38 |     expect(one).toEqual(two);
         |                 ^
      39 |
      40 |   });
      41 | });

      at Object.<anonymous> (../tests/app.test.ts:38:17)

Test Suites: 1 failed, 1 total

I can very quickly pin point the exact issue due to this intelligent diff. In the terminal this reads even nicer due to better colour coding.

On the other hand if I have this equivalent C++ code:

struct Second {
    bsl::vector<char> third;
  // Assume operator== is defined
};
struct First {
    Second second;
    int anotherOne;
  // Assume operator== is defined
};

struct Example {
    bsl::string foo;
    First first;
  // Assume operator== is defined
};

TEST(Example, one) {
    auto const one = Example{
        .foo = "bar",
        .first = {
          .second = {
            .third = {'a', 'b', 'c'}
          },
          .anotherOne = 42
        }
    };
    auto const two = Example{
        .foo = "bar",
        .first = {
          .second = {
            .third = {'a', 'b', 'd'}
          },
          .anotherOne = 43
        }
    };

    EXPECT_EQ(one, two);
}

I get the following output which is not helpful at all in finding the actual differences.

[ RUN      ] Example.one

../src/[...].t.cpp:312: Failure
Expected equality of these values:
  one
    Which is: { foo: "bar", first: { second: { third: { 'a' (97, 0x61), 'b' (98, 0x62), 'c' (99, 0x63) } }, anotherOne: 42 } }
  two
    Which is: { foo: "bar", first: { second: { third: { 'a' (97, 0x61), 'b' (98, 0x62), 'd' (100, 0x64) } }, anotherOne: 43 } }
[  FAILED  ] Example.one (0 ms)

In larger diffs, this can become really frustrating. Of course, I could define some helper where I dig into the nested structure of the Example class and print where exactly there are differences.

I was wondering if gtest could provide some generic solution which, similar to Jest, does that deep and intelligent comparison for me.

Denis-Gavrielov avatar Mar 09 '21 16:03 Denis-Gavrielov

JS has the advantage of having a clear interface for type visitation, namely Object properties. That said, IIRC gtest is smart enough to identify when the textual representations are identical, so it's perhaps not too much of a stretch to make it do a "naive" diff of the textual representation as a first improvement in this area.

In #1301 I've raised the issue of factoring out the gtest-printers interface; perhaps there's a case to go further and express it in terms of structured values instead, which may be used for pretty-printing arbitrarily, and doing these kinds of "representation"-level Spot The Difference games.

alexweej avatar Mar 09 '21 16:03 alexweej

This is a good feature request, but it is fairly hard to do in C++. I doubt we will have the resources to look into this for a while.

derekmauro avatar Mar 15 '21 19:03 derekmauro

Thanks for taking a look at this.

It would be nice to get updates once someone might be making progress on this feature or features which may make this request easier to solve.

Denis-Gavrielov avatar Mar 15 '21 22:03 Denis-Gavrielov

What if you took the string representation that would be printed anyway and just did a text diff on it?

LegalizeAdulthood avatar May 30 '23 21:05 LegalizeAdulthood