Kevin Grady
03/20/2023, 11:22 PMthe mockService property on the Pact class is no longer an actual MockService, but supports the baseUrl property for compatibility.
But I am seeing an error that mockService is not a known property of PactV3. What are my options here?Yousaf Nabi (pactflow.io)
executeTest
scope as shown in the linked docs
so this
const api = new API(provider.mockService.baseUrl);
// make request to Pact mock server
const product = await api.getAllProducts();
expect(product).toStrictEqual([
{"id": "09", "name": "Gem Visa", "type": "CREDIT_CARD"}
]);
would become this
await provider.executeTest(async (mockService) => {
const api = new API(mockService.url);
// make request to Pact mock server
const product = await api.getAllProducts();
expect(product).toStrictEqual([
{"id": "09", "name": "Gem Visa", "type": "CREDIT_CARD"}
]);
})
Erich Buri
03/22/2023, 2:30 PM@Injectable()
export class DynamicUrlTestInterceptor implements HttpInterceptor {
constructor(private urlResolver: () => string | undefined) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const urlToPrepend = this.urlResolver();
if (urlToPrepend) {
request = request.clone({
url: urlToPrepend + request.url,
});
}
return next.handle(request);
}
}
Add it to your Test-Env:
{
provide: HTTP_INTERCEPTORS,
useValue: new DynamicUrlTestInterceptor(() => mockServerUrl),
multi: true,
},
In my test-case:
const angebotIntegrationService = TestBed.inject(AngebotIntegrationService);
await providerV3.executeTest(async (mockServer) => {
mockServerUrl = mockServer.url; // activate DynamicUrlTestInterceptor
const response = await firstValueFrom(
angebotIntegrationService.createAngebotNeugeschaeftRsvUnternehmen(
angeboteErstellenNeugeschaeftRsvUnternehmen,
captchaToken
)
);
Erich Buri
03/22/2023, 2:38 PMlet mockServerUrl: string | undefined = undefined;
Basically, I set the mockServerUrl = mockServer.url;
in executeTest()
, for any subsequent Http-Request, the Interceptor will be called, which will in turn call the urlResolver
callback to get the current-value to use at that moment, and change the url accordingly.Erich Buri
03/22/2023, 2:38 PMKevin Grady
03/22/2023, 8:50 PMmockServerUrl
being passed? If I assign it within executeTest()
it has no place to go and is just an unused variableKevin Grady
03/22/2023, 8:57 PMconst api = new API(mockService.url);
but takes an HttpService
and HttpConfigService
which have been added as providers in a beforeEach()
.
If that is the case, can I simply leave the API instantiation out of the executeTest()
method? I still need access to a mockUrl for the provider `ConfigService`’s useValue
Kevin Grady
03/22/2023, 8:59 PMbeforeEach
untouched. Error is on mockProvider.mockService
which no longer exists:
beforeEach(async () => {
const app = await Test.createTestingModule({
imports: [ConfigModule, HttpModule],
providers: [
TenantService,
HttpConfigService,
{
provide: 'MOCK_ENV',
useValue: false,
},
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === 'TENANT_BASE_URL') {
return mockProvider.mockService.baseUrl;
}
}),
},
},
],
}).compile();
service = app.get<TenantService>(TenantService);
});
Erich Buri
03/23/2023, 9:37 AMHttpService
- we're using HttpClient
.
But looking at your setup, you would have to return the actual value here:
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === 'TENANT_BASE_URL') {
return mockProvider.mockService.baseUrl;
}
}),
},
Except that you use a variable like mockServerUrl
in my case (instead of mockProvider.mockService.baseUrl
)
mockServerUrl will then be set inside executeTest()
Kevin Grady
03/23/2023, 6:08 PMprovide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === 'TENANT_BASE_URL') {
return new DynamicUrlTestInterceptor(() => mockServerUrl);
}
}),
},
after implementing the DynamicUrlTestInterceptor. I assign the mockServerUrl in executeTest()
but then the mockServerUrl isn’t referenced after it is assigned so it seems useless:
await mockProvider.executeTest(async mockServer => {
mockServerUrl = mockServer.url;
const tenant = await lastValueFrom(service.createTenant(requestHeaders, reqBody));
expect(tenant).toStrictEqual(camelCaseObjectKeys(responseBody));
});
The test result in a connection error I assume is caused by the Url not working properly:
+ "code": "ECONNREFUSED",
+ "data": undefined,
+ "message": "connect ECONNREFUSED ::1:80",
+ "status": undefined,
+ "statusText": undefined,
Erich Buri
03/27/2023, 9:30 AMprovide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === 'TENANT_BASE_URL') {
return new DynamicUrlTestInterceptor(() => mockServerUrl);
}
}),
},
Erich Buri
03/27/2023, 9:35 AM{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === 'TENANT_BASE_URL') {
return mockProvider.mockService.baseUrl;
}
}),
},
Did you try to return the variable mockServerUrl here? You need to show me your Service-Code or find out at what time in the Lifecycle of your service it will call configService.get('TENANT_BASE_URL').
Anyway, if your Service eventually uses Angular HttpClient, you can just add this.
{
provide: HTTP_INTERCEPTORS,
useValue: new DynamicUrlTestInterceptor(() => mockServerUrl),
multi: true,
},
Replace () => mockServerUrl
with () => { console.log('Will now use url: ', mockServerUrl); return mockServerUrl }
and you will see in your log at what time the Url actually gets used.Kevin Grady
03/27/2023, 6:22 PMDid you try to return the variable mockServerUrl here?Yes I had the same error results
Anyway, if your Service eventually uses Angular HttpClient, you can just add this.I’m getting a type mismatch error:
Type 'InjectionToken<HttpInterceptor[]>' is not assignable to type 'InjectionToken'.
Type 'InjectionToken<HttpInterceptor[]>' is missing the following properties from type 'Abstract<any>': prototype, apply, call, bind, and 5 more.
our main app module is using HttpClient but HttpConfigService is not. The HttpConfigService just determines if we are using local path for dummy data or not, for example:
constructor(@Inject(MOCK_ENV) private mockEnv: boolean, private configService: ConfigService) {}
private baseUrl = this.configService.get('TENANT_BASE_URL');
private tenantUrl = `${this.baseUrl}/tenant`;
public getTenant(): string {
const config: httpConfig = {
path: this.tenantUrl,
localPath: '/get-tenant.json',
};
return this.getPath(config);
}
which is where configService.get(’TENANT_BASE_URL) is being calledKevin Grady
04/03/2023, 4:28 PMErich Buri
04/04/2023, 11:37 AMlet mockServerUrl = undefined;
beforeEach(async () => {
mockServerUrl = undefined;
const app = await Test.createTestingModule({
imports: [ConfigModule, HttpModule],
providers: [
TenantService,
HttpConfigService,
{
provide: 'MOCK_ENV',
useValue: false,
},
{
provide: HTTP_INTERCEPTORS,
useValue: new DynamicUrlTestInterceptor(() => mockServerUrl),
multi: true,
},
...
In your test:
await mockProvider.executeTest(async mockServer => {
mockServerUrl = mockServer.url;
const tenant = await lastValueFrom(service.createTenant(requestHeaders, reqBody));
expect(tenant).toStrictEqual(camelCaseObjectKeys(responseBody));
});
There's nothing I can add to that. The Injection-Token HTTP_INTERCEPTORS and the Base-Class HttpInterceptor are from Angular itself. Maybe add a console.log()-Statement in the intercept()-Method of the DynamicUrlTestInterceptor to better understand what happens and to verify that the actual request comes through there.