Hello! Could you please help me to understand why ...
# pact-js
t
Hello! Could you please help me to understand why I cannot generate a contract, why I am getting the following error:
Copy code
The following request was expected but not received: 
            Method: GET
            Path: /events/1cb9eb9e
We are using React Testing Library and jest for testing. I am using PactV3 for contract testing. My code:
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider'
});
  
const EXPECTED_BODY = {
  "name": "test event",
  "id": "1cb9eb9e",
  "teamId": "dummy_tid",
};

function flush() {
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

function createWrapper({ children }: { children: React.ReactElement }) {
  return <Provider store={makeStore()}>{children}</Provider>;
}
  
describe('GET /event/{id}', () => {
  it('returns an event', async () => {
    provider
      .given('I can get an event')
      .uponReceiving('a request for one event with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/events/1cb9eb9e'
      })
      .willRespondWith({
        status: 200,
        body: EXPECTED_BODY,
      });

      return provider.executeTest(async () => {
        //Arrange - we are using env var to provide a base url
        process.env.API_HOST = '<http://localhost:8082>';
        console.log(process.env.API_HOST);

        //Act
        const { result } = renderHook( () => useGetEventQuery({ id: '1cb9eb9e' }),{
          wrapper: createWrapper,
        });
        
        await flush();

        console.log(result.current);

        // Assert: check the result
        expect(result.current.data?.id).toBe("1cb9eb9e");
    });
  });
});
I receive an undefined data, the
console.log(result.current);
returns:
Copy code
{
      status: 'pending',
      endpointName: 'getEvent',
      requestId: '6OJC915gB1DnUNUKk7zPP',
      originalArgs: { id: '1cb9eb9e' },
      startedTimeStamp: 1664282999059,
      isUninitialized: false,
      isLoading: true,
      isSuccess: false,
      isError: false,
      data: undefined,
      currentData: undefined,
      isFetching: true,
      refetch: [Function: refetch]
    }
Shouldn’t the data be set by Pact mock server? Please help me to understand what I am doing wrong and why my request is not being received by the mock server🙏
I’ve seen this message: https://pact-foundation.slack.com/archives/C9VBGLUM9/p1660526266380169 and the response:
Copy code
Ok. I can't help you other than tell you that the error message means that your request is not being received by the mock server. Either you're not sending it, or it's not going to the right place, or you are sending it, but it is not what you set up in the test, or your test isn't waiting for the request to be sent before asking pact if it was sent.
1. I can say that I send my request, it’s here:
Copy code
const { result } = renderHook( () => useGetEventQuery({ id: '1cb9eb9e' }),{
          wrapper: createWrapper,
        });
2. also, I am waiting for the request is done, I use
await flush()
function after the request 3. As I can see, I send and try to get a correct data, I checked the response schema and I have all these provided fields. 4. I can’t get this
it's not going to the right place
, how I can check it?
y
The pact mock provider address is available via
mockserver.url
https://github.com/pact-foundation/pact-js/blob/master/docs/consumer.md I assume your code is making requests to
Copy code
<http://localhost:8082>
looking at your code, in setting the env var, but the pact mock provider will be listening on a different port
the host defaults to localhost, but the port is randomly assigned. You can override this in the PactV3 constructor options
t
I use
mockserver.url
now, such as
Copy code
return provider.executeTest(async (mockserver) => {
        //Arrange
        process.env.API_HOST = mockserver.url;
        console.log(process.env.API_HOST); //returns <http://127.0.0.1:58968>

        //Act
        const { result } = renderHook( () => useGetEventQuery({ id: '1cb9eb9e' }), {
          wrapper: createWrapper,
        });
        
        await flush();

        console.log(result.current.data);

        // Assert: check the result
        expect(result.current.data?.id).toBe(EXPECTED_BODY.id);
    });
but still having the issue
The following request was expected but not received
Any ideas what might be wrong? 😞
y
Would imagine that process.env isn’t making effect on your client code and it’s still making a request to whatever it’s default url is. Can you provide a minimal example on GitHub or your code under test and not just the test code? Ty
t
I cannot send you a link to repo, but I can send you code snippets. it’s how we send a request:
Copy code
import { baseApi } from './baseApi';
import {
  EventResponseSchema,
  EventId,
} from './types';

export const eventsApi = baseApi
  .enhanceEndpoints({ addTagTypes: ['Events'] })
  .injectEndpoints({
    endpoints: (builder) => ({
      getEvent: builder.query<EventResponseSchema, { id: EventId }>({
        query: ({ id }) => `events/${id}`,
        providesTags: (result, error, { id }) => [{ type: 'Events', id }],
      }),
    }),
  });

export const {
  useGetEventQuery,
} = eventsApi;
Response schema is:
Copy code
export interface EventResponseSchema {
  id: string;
  name: string;
  teamId: string;
  updatedAt: string;
  createdAt: string;
  duration?: number;
  ...
  ...
}

export type EventId = string;
baseApi
Copy code
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react';

const apiHost = process.env.API_HOST;

export const baseApi = createApi({
  reducerPath: 'services',
  baseQuery: fetchBaseQuery({
    baseUrl: apiHost ?? '/api',
    mode: 'cors',
    credentials: 'include',
  }),
  endpoints: () => ({}),
});
and by default we set
process.env.API_HOST
in the
src/setupJestEnv.ts
Copy code
process.env.API_HOST = '<http://localhost:8000>';
y
try setting the port in the PactV3 constructor, so your port used, matches up.
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider',
  port: 8000
});
Also I don't think your request has completed, as the response object status 'isLoading: true' and isSuccess/isLoading is false
👀 1
t
I’ve added port and added
waitForNextUpdate
to my request, such as
Copy code
const { result, waitForNextUpdate } = renderHook( () => useGetEventQuery({ id: '1cb9eb9e' }), {
         wrapper: createWrapper,
});
        
await waitForNextUpdate();
and now I am getting
isLoading:false
and the following error:
Copy code
{
      status: 'rejected',
      endpointName: 'getEvent',
      requestId: 'K3YytVNiG5C7LnhFAtr07',
      originalArgs: { id: '1cb9eb9e' },
      startedTimeStamp: 1664288258291,
      error: {
        status: 'FETCH_ERROR',
        error: 'FetchError: request to <http://localhost:8000/events/1cb9eb9e> failed, reason: connect ECONNREFUSED ::1:8000'
      },
      isUninitialized: false,
      isLoading: false,
      isSuccess: false,
      isError: true,
      data: undefined,
      currentData: undefined,
      isFetching: false,
      refetch: [Function: refetch]
    }
if I don’t provide port, I see that
Copy code
console.log(process.env.API_HOST); //returns <http://127.0.0.1:59891>
but error says the same as above:
Copy code
error: {
        status: 'FETCH_ERROR',
        error: 'FetchError: request to <http://localhost:8000/events/1cb9eb9e> failed, reason: connect ECONNREFUSED ::1:8000'
      },
I would expect the port would be different rather than 8000
can it happen because Pact mock server is not set on the 8000 port?
y
yes the pact mock server, as per the documentation link I sent you, sets the port to a randomly assigned value, your application configuration isn't picking up the process.env.value set in your test
Therefore the fetch error is correct, as you have no service running on port 8000. You can set this value directly in the pact configuration setup
👀 1
t
sorry, do I understand correctly that I just need to provide a
host
for my provider setup? Like this:
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider',
  host: process.env.API_HOST
});
if so, should I use the following test setup then?
Copy code
return provider.executeTest(async (mockserver) => {
        //Arrange
        process.env.API_HOST = mockserver.url;
        console.log(process.env.API_HOST);
no, it’s a Pact constructor, looks like a wrong place. If you have any example how pact configuration can be set, please share it with me 🙏
y
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider',
  port: 8000
});
you wont need this
Copy code
process.env.API_HOST = mockserver.url;
        console.log(process.env.API_HOST);
👀 1
t
unfortunately, the error still exists
Copy code
error: {
        status: 'FETCH_ERROR',
        error: 'FetchError: request to <http://localhost:8000/events/1cb9eb9e> failed, reason: connect ECONNREFUSED ::1:8000'
      },
My tests looks like this now:
Copy code
const provider = new PactV3({
  dir: path.resolve(process.cwd(), 'pacts'),
  consumer: 'MyConsumer',
  provider: 'MyProvider',
  port: 8000,
});
  
const EXPECTED_BODY = {
  name: "test event dd",
  id: "1cb9eb9e",
  teamId: "dummy_tid",
};

function flush() {
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

function createWrapper({ children }: { children: React.ReactElement }) {
  return <Provider store={makeStore()}>{children}</Provider>;
}
  
describe('GET /events/{id}', () => {
  it('returns an event', async () => {
    provider
      .given('I can get an event')
      .uponReceiving('a request for one event with the builder pattern')
      .withRequest({
        method: 'GET',
        path: '/events/1cb9eb9e',
      })
      .willRespondWith({
        status: 200,
        body: EXPECTED_BODY,
      });

      return provider.executeTest(async () => {
        //Act
        const { result, waitForNextUpdate } = renderHook( () => useGetEventQuery({ id: '1cb9eb9e' }), {
          wrapper: createWrapper,
        });
        
        await waitForNextUpdate();

        console.log(result.current);

        // Assert
        expect(result.current.data?.id).toBe(EXPECTED_BODY.id);
    });
  });
});
any ideas why it doesn’t work? 😞
ok, I was able to generate a contract. I just overwritten a default value for my
process.env.API_HOST = '<http://127.0.0.1:8001>';
in the src/setupJestEnv.ts. Thank you for your support and quick response! you helped me a lot! 🙌
t
I wouldn’t expect to see react in your API test code- it is better practice to test your api layer outside of react
☝️ 1
t
what’s is the biggest drawback of using react in the tests?
t
Unnecessarily complicated test setup, and since react is unrelated to the api code, if your components or hooks change the test will unnecessarily break or need to be updated
Usually your hook has some call to an API function in it. Your test would exercise that function, and then your hook becomes just translating between that function and react
👀 1
t
good point, thank you for the explanations. I’ll think how I can to avoid of using react in my tests. Thanks a lot! 🙌
🎉 1