ballerina-platform / ballerina-lang

The Ballerina Programming Language
https://ballerina.io/
Apache License 2.0
3.66k stars 751 forks source link

[Bug]: Cannot mock `http:Client` by creating test double #41821

Closed niveathika closed 1 month ago

niveathika commented 10 months ago

Description

The code given in https://ballerina.io/learn/test-ballerina-code/mocking/#create-a-test-double is not working.

remote isolated function get(string path, map<string|string[]>? headers = (),
            http:TargetType targetType = http:Response) returns http:Response  |http:ClientError {

The above results in

error: incompatible return type provided for function 'get()'
        at ballerina.test.0:mock(mock.bal:309)
           ballerinax.googleapis.gmail.oas$test.4.tests.test:getMockClient(tests/test.bal:29)
           ballerinax.googleapis.gmail.oas.4.Client:init(client.bal:43)
remote isolated function get(string path, map<string|string[]>? headers = (),
            http:TargetType targetType = http:Response) returns http:Response | anydata |http:ClientError {

This results in following error,

error("{ballerina}TypeCastError",message="incompatible types: 'http:Response' cannot be cast to 'error'")
                                callableName: $get$users$^$profile moduleName: ballerinax.googleapis.gmail.oas.4.Client fileName: client.bal lineNumber: 700
                                callableName: testGmailGetProfile moduleName: ballerinax.googleapis.gmail.oas$test.4.tests.test fileName: tests/test.bal lineNumber: 34
                                callableName: testGmailGetProfile$lambda0$ moduleName: ballerinax.googleapis.gmail.oas$test.4.tests.test_execute-generated_1 fileName: tests/test_execute-generated_1.bal lineNumber: 4

Steps to Reproduce

No response

Affected Version(s)

No response

OS, DB, other environment details and versions

No response

Related area

-> Test Framework

Related issue(s) (optional)

No response

Suggested label(s) (optional)

No response

Suggested assignee(s) (optional)

No response

Dilhasha commented 10 months ago

I was able to reproduce the mentioned error with the following example.

main.bal

import ballerina/http;
import ballerina/regex;

http:Client clientEndpoint = check new ("https://api.chucknorris.io/jokes/");

type Joke readonly & record {|
string value;
|};

// This function performs a `get` request to the Chuck Norris API and returns a random joke
// with the name replaced by the provided name or an error if the API invocation fails.
function getRandomJoke(string name) returns string|error {
Joke joke = check clientEndpoint->get("/random");
string replacedText = regex:replaceAll(joke.value, "Chuck Norris", name);
return replacedText;
}

main_test.bal

import ballerina/test;
import ballerina/http;

// An instance of this object can be used as the test double for the `clientEndpoint`.
public client class MockHttpClient {

remote function get(string path, map<string|string[]>? headers = (), http:TargetType targetType = http:Response) returns http:Response|anydata|http:ClientError {
// Joke joke = {"value": "Mock When Chuck Norris wants an egg, he cracks open a chicken."};
return {"value": "Mock When Chuck Norris wants an egg, he cracks open a chicken."};
}

}

@test:Config {}
public function testGetRandomJoke() {

// create and assign a test double to the `clientEndpoint` object
clientEndpoint = test:mock(http:Client, new MockHttpClient());

// invoke the function to test
string|error result = getRandomJoke("Sheldon");

// verify that the function returns the mock value after replacing the name
test:assertEquals(result, "Mock When Sheldon wants an egg, he cracks open a chicken.");
}
Dilhasha commented 10 months ago

The issue occurs when you try to return the record directly as follows. return {"value": "Mock When Chuck Norris wants an egg, he cracks open a chicken."};

But if you assign it to a variable and return, this error can be avoided.

Joke joke = {"value": "Mock When Chuck Norris wants an egg, he cracks open a chicken."};
return joke;

@niveathika Until we fix the issue can you use it as a workaround?

niveathika commented 10 months ago

Ack. I will use this workaround for the time being

gayaldassanayake commented 10 months ago

Faced a similar issue when mocking an http:Client with a post(). Assigning and returning worked fine, but we might at least need to document this.

Dilhasha commented 1 month ago

Closing this since a workaround is provided.

github-actions[bot] commented 1 month ago

This issue is NOT closed with a proper Reason/ label. Make sure to add proper reason label before closing. Please add or leave a comment with the proper reason label now.

      - Reason/EngineeringMistake - The issue occurred due to a mistake made in the past.
      - Reason/Regression - The issue has introduced a regression.
      - Reason/MultipleComponentInteraction - Issue occured due to interactions in multiple components.
      - Reason/Complex - Issue occurred due to complex scenario.
      - Reason/Invalid - Issue is invalid.
      - Reason/Other - None of the above cases.