question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problem with combine() and Mocks

See original GitHub issue

I just spent the last few days chasing my tail on this one. I was using several mocking libraries, notably ts-mockito and testdouble, and got the same behavior. If I created a mock object, wrapped it with okAsync(), and tossed that into combine(), I would get nothing in the return- the mock object was being dropped. Here’s an example test in jest that would fail:

import td from "testdouble";

interface ITestInterface {
    getName(): string;
    setName(name: string): void;
    getAsyncResult(): ResultAsync<ITestInterface, Error>;
}

describe("Debugging and basic info tests", () => {
test("combine works with TestDouble mocks of interfaces", async () => {
    // Arrange
    const mock = td.object<ITestInterface>();

    // Act
    const result = await combine([okAsync(mock)]);

    // Assert
    expect(result).toBeDefined();
    expect(result.isErr()).toBeFalsy();
    const unwrappedResult = result._unsafeUnwrap();
    expect(unwrappedResult.length).toBe(1);
    expect(unwrappedResult[0]).toBe(mock);
  });
});

If you run this, you’d find that the last two expect calls would fail, because combine would return an empty array! It would only do this with mocks, not with normal objects. So I dug into it, and I found a solution in the combine() code. You are using array.concat(nonArray) in the reduce() of combineResults(). I rolled my own combine and it works even with mocks. I added typing support for heterogenous lists as well, but it looks like you’re working on a slicker solution in another issue. Here’s my “fixed” combine:

export class ResultUtils {
    static combine<T, T2, T3, T4, E, E2, E3, E4>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>, ResultAsync<T3, E3>, ResultAsync<T4, E4>]): ResultAsync<[T, T2, T3, T4], E | E2 | E3 | E4>;
    static combine<T, T2, T3, E, E2, E3>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>, ResultAsync<T3, E3>]): ResultAsync<[T, T2, T3], E | E2 | E3>;
    static combine<T, T2, E, E2>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>]): ResultAsync<[T, T2], E | E2>;
    static combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[],E> {
        return ResultAsync.fromPromise(Promise.all(asyncResultList), 
        (e) => {return e as E})
        .andThen(ResultUtils.combineResultList);
    }
    
    static combineResultList<T, E>(resultList: Result<T, E>[]): Result<T[], E> {
        return resultList.reduce((acc: Result<T[], E>, result) => {
            return acc.isOk()
                ? result.isErr()
                    ? err(result.error)
                    : acc.map((values) => {
                        values.push(result.value);
                        return values;
                    })
                : acc;
        }, ok([]));
    };
}

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:21 (16 by maintainers)

github_iconTop GitHub Comments

1reaction
kieran-osgoodcommented, Nov 19, 2021

@supermacro That’s great! How do you usually do this, I can give you my twitter to speak easier? https://twitter.com/kieranbosgood

1reaction
supermacrocommented, Nov 15, 2021

Hey @kieran-osgood, sorry for the delay in responding.

I’ll be sinking my teeth into this issue and your response once again this week.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Method to generate error (failure) for a SwiftUI Combine ...
My question is how to generate an error (failure) in my code? I only found some code sample regarding with error handling. But...
Read more >
Full support of Combine · Issue #235 · birdrides/mockingbird
If the generator produces code that is malformed or does not compile, please provide: A minimal example of the original source. import ...
Read more >
How to map error in Combine | Swift Discovery - onmyway133
Issue #506 When a function expects AnyPublisher<[Book], Error> but in mock, we have Just func getBooks() -> AnyPublisher<[Book], ...
Read more >
Best Practices for Spies, Stubs and Mocks in Sinon.js
Together, spies, stubs and mocks are known as test doubles. Similar to how stunt doubles do the dangerous work in movies, we use...
Read more >
Mocks Aren't Stubs - Martin Fowler
getTheOther() . Avoiding method chains is also known as following the Law of Demeter. While method chains are a smell, the opposite problem...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found