71

I need help writing a common function to use across a collection of requests which will help with building a framework.

I have tried using the below format

The following function is declared in the Test tab in the first function

postman.setGlobalVariable("function", function function1(parameters)
{
  //sample code
});

I used the following in the pre-request

var delay = eval(globals.function);
delay.function1(value1);

I am getting the following error

there was error while evaluating the Pre-request script : Cannot read property 'function1' of undefined.

Can anyone help me with how to define Global/common functions and use them across the requests?

Thanks in advance

JBC
  • 667
  • 1
  • 9
  • 21
Anji R
  • 843
  • 3
  • 16
  • 23
  • 5
    Related: collection-scoped scripts are now supported per EOY 2017 - http://blog.getpostman.com/2017/12/13/keep-it-dry-with-collection-and-folder-elements/ – aff Mar 22 '18 at 13:44

11 Answers11

102

Without eval:

Define an object containing your function(s) in the collection's pre-request scripts without using let, var, etc. This attaches it to Postman's global sandbox object.

utils = {
  myFunc: function() {
    return 'hello';
  }
};

Then within your request's pre-request or test script section just call the function:

console.log(utils.myFunc());
ggutenberg
  • 6,880
  • 8
  • 39
  • 48
  • 4
    This should be the answer. It achieves all that the OP was after (globally-accessible functions), in the most succinct way, and without using `eval()`. A quick Google will tell you that `eval()` is generally avoided by devs for security, performance and debugability reasons. – Hoopla Jul 02 '20 at 08:36
  • 13
    This works until you need to do something like `pm.environment.set('foo', 'bar');` within one of those functions. It runs without error but the variable never gets set. – Jake Jul 29 '20 at 23:34
  • 1
    @Jake I haven't tested this because I haven't used Postman lately, but I'm curious to know if the issue is just that the global context doesn't have access to the environment context? Maybe `pm.variables.set()` or `pm.globals.set()` would still work? – ggutenberg Aug 08 '20 at 15:16
  • 4
    @jake I found the same issue that you described. For global functions I just pass pm as an argument to the function and that is working for me in my use cases. Obviously might get a bit messy if I need other stuff that is also not in scope. – bentayloruk Oct 10 '20 at 10:22
  • Amazing. Thank you! Found this via https://javascriptio.com/view/116768/how-to-write-global-functions-in-postman and came back to share/credit only to find it'd missed it here the first time around :D – rainabba Oct 27 '20 at 22:22
  • Nice solution which covers a few use-cases. I am looking for a way to adjust this code to export `myFunc` when it calls `pm.sendRequest` with a callback - I can't get Postman to wait for the callback execution when I call `utils.myFunc()` in a request. Any ideas on what needs modifying? Thanks. – OG Dude Feb 28 '22 at 16:43
  • @OGDude - might be better if you posted your code as a new question. – ggutenberg Mar 01 '22 at 17:30
  • 5
    I pass in the pm to each and every method of this global var. In that way I know it's the same one that is present in the calling request, and can do things like `pm.environment.set('foo', 'bar');` – monty Mar 10 '22 at 22:24
  • Painfully, `function foo() {}` doesn't get added to the "global sandbox" either. You have to `foo = function () {}`, which pains my lint-trained brain in any number of ways. But this answer worked; thanks. – ruffin Sep 29 '22 at 14:47
  • @bentayloruk Please elaborate how you pass pm to global functions – DeineOma420 Feb 08 '23 at 17:41
  • 1
    As suggested, always pass in pm and you can do anything. utils = { myFunc: function(pm) { pm.environment.set("yourvar", "somevalue"); return 'hello'; } }; – Tim Hallbeck Mar 05 '23 at 20:39
35

I use this little hack:

pm.globals.set('loadUtils', function loadUtils() {
    let utils = {};
    utils.reuseableFunction = function reuseableFunction() {
        let jsonData = JSON.parse(responseBody);
    }
    return utils;
} + '; loadUtils();');
tests['Utils initialized'] = true;

In another request I can reuse the global variable loadUtils:

const utils = eval(globals.loadUtils);
utils.reuseableFunction();

You can also check the developer roadmap of the Postman team. Collection-level scripts are on the near-term agenda and should be available soon until then you can use the shown method.

Sixto Saez
  • 12,610
  • 5
  • 43
  • 51
Sergej Lopatkin
  • 1,071
  • 9
  • 11
  • 3
    Note: be careful with adding `JSON.parse(responseBody);` or pm.response.json(); to your utils, as: Firstly, it won't work in Pre-request script, as response object cannot be accessed from there. Secondly, it may not work in Tests tab too in a case where no JSON is available in the response body: for example, XML or HTML. In the case of referring to the response object, should consider having separate utils for Tests and Pre-request scripts. – n-verbitsky Sep 20 '19 at 14:27
22

Edit: The following answer is still valid and you can skip ahead and read it, but I want to give a warning first: If you are trying use this in Postman, you should probably use something else than Postman, like Mocha, for your testing. Postman is OK for small to medium scale applications but very large multi-developers applications can be a nightmare to maintain with postman. The in-app editor is a mess for large files, and versioning can be problematic.

ANSWER
You can have a more readable solution and more possibility to factor your code (like calling function1() from function2() directly inside your pre-request script, or declaring packages) with the following syntax :

Initialize environment (or globals) :

postman.setEnvironmentVariable("utils", () => {
    var myFunction1 = () => {
        //do something
    }
    var myFunction2 = () => {
        let func1Result = myFunction1();
        //do something else
    }
    return {
        myPackage: {
            myFunction1,
            myFunction2
        }
    };
});

And then use your functions in a later test :

let utils = eval(environment.utils)();
utils.myPackage.myFunction1(); //calls myFunction1()
utils.myPackage.myFunction2(); //calls myFunction2() which uses myFunction1()

Bonus :

If you are calling an API and need to wait the call to finish before performing a test, you can do something like this:

postman.setEnvironmentVariable("utils", () => {
    var myFunction = (callback) => {
        return pm.sendRequest({
            // call your API with postman here
        }, function (err, res) {
            if (callback) {
                //if a callback method has been given, it's called
                callback();
            }
        });
    }
    
    return {
        myPackage: {
            myFunction,
        }
    };
});

and then to use it:

utils.myPackage.myFunction(function() {
    console.log("this is the callback !")
    //perform test here
});
Tom
  • 1,357
  • 2
  • 13
  • 32
9

Very easy to achieve with monkey patch on object (or whatever you want).

Option #1

On collection level pre script

Object.prototype.doSomething = (foo) => console.log(foo);

On any other place:

pm.doSomething('bar');

// or

postman.doSomething('bar');

Option #2

On collection level pre script

Utilities = {};
Utilities.getParam = (foo) => console.log(foo);

On any other place

Utilities.getParam('bar');
Gravity API
  • 680
  • 8
  • 16
8

If you want to call pm.sendRequest in a global function, try this:

  1. Define the global function in collection pre-request, like this:

    pm.globals.set('globalFunction', parameters => {
        console.log(parameters);
        pm.sendRequest('https://google.com/', function(err, resp) {
            pm.expect(err).to.not.be.ok;
        });
    });
    
  2. Use function like this:

    eval(globals.globalFunction)('hello world!!');

Note that, I declared function using arrow style ()=>{}. Otherwise, it wouldn't work.

n-verbitsky
  • 552
  • 2
  • 9
  • 20
kamran ghiasvand
  • 836
  • 2
  • 11
  • 19
  • 4
    This doesn't work for me. I'm getting `There was an error in evaluating the Pre-request Script: TypeError: eval(...) is not a function`. – 303 Aug 25 '20 at 13:12
6

The problem had perplexed me for a while until I found the common way mentioned above. However, it still leaves a warning icon for each eval line, which indicates “eval can be harmful” in the postman interface. Recently, I’ve found another way and post it here: Users can create a prototype object with the proper function you want in the pre-request script section, like this:

Object.prototype.sayHello = function(name){
console.log(`Hello! ${name}`);
};

and call that function everywhere after that. It just required a defined object, like this:

let obj = {};
obj.sayHello(‘Griffin’);

Or you don’t even need the declaration of the object but use some built-in objects instead, like lodash (you pretend it has the function :smile: )

_.sayHello(‘Griffin’);

It’s working on my side. I also posted it in postman forum here https://community.postman.com/t/global-functions-via-collection-level-folder/5927/6?u=franksunnn110

franksunnn
  • 147
  • 2
  • 9
  • Works! But to call the function declared in this way in the collection script from a request script, all that is needed is the function name. e.g. sayHello('Griffin');. – Will P Nov 07 '20 at 06:56
  • 2
    This is the worst hack I have seen in flipping ages, but it works. It's the only way I have managed to get it working to share code between pre-requests. After all this time it's ridiculous that POSTMAN don't have something to share code. – Jim Aug 30 '21 at 10:46
4

A more elegant way consists of using the global context (not variables) in the pre-request scripts. For example, if you wish a function called myFunction to be available in any pre-request script, you can go to Edit Collection/Pre-request and set

globalThis.myFunction = function(){}

Then you can call myFunction in any pre-request script of any request inside the collection (without any stringification/evaluation of your function)

Luca Marzi
  • 786
  • 2
  • 8
  • 24
1

You can declare a global function by assigning this function into a collection, environment or global variable as follows:

  • Create a collection variable, i.e. global_func
  • In the variable value write this code,

(number)=> { return number * number }

to reuse this function elsewhere in your collection

let numberSquared = eval(pm.variables.get('global_func'))(5)

now, numberSqaure variables has a value of 25

================================

if you need to declare a function library: you can create a collection variable and assign it this piece of code:

({
    print:function() {
        console.log('hello Postman')
    },
    squared:function(number) {
        return number * number
    }
})

Note: the functions have been enclosed with parentheses

to reuse this library:

let lib = eval(pm.variables.get('global_func'))
lib1.print()
console.log(lib1.squared(4))

Good luck :)

Amado Saladino
  • 2,114
  • 23
  • 25
0

Define functions as a global variables then access across all of your tests.

pm.environment.set("UTILS", `({ myFunction: (text) => console.log(text) })`)

Call the functions

let utils = eval(pm.environment.get("UTILS"))
utils.myFunction('Test')
Googlian
  • 6,077
  • 3
  • 38
  • 44
0

A lot of the above examples will have issues with pm variable. In order to have access to pm variable inside your custom utils method you need to pass it inside after eval. On top of that it is nice to have some modularity for your utils. Paste this as a value for a global variable under name -> Util

(function () {
    const Util = function (pm) {
        return {
            customUtilFunction: function (customVariable) {
                pm.environment.set("customVariable", customVariable);
            }
        }
    }
    return Util
}())

And then inside your test you can call it like presented below.

const Util = eval(pm.globals.get('Util'))(pm)
Util.customUtilFunction('myCustomVariableValue')
twboc
  • 1,452
  • 13
  • 17
  • Just tried this and it doesn't work. Even tried modifying it a couple different ways and still couldn't get it working. `TypeError: eval(...) is not a function` – tehbeardedone Aug 05 '21 at 15:46
  • Fist you should check what does the pm.globals.get('Util') return. The error clearly says that 'is not a function'. Could you past the result of console.log(pm.globals.get('Util')) somewhere? – twboc Aug 06 '21 at 08:29
0
pm.globals.set("compare_obj",function compare_obj(){
    return (response,outputName)=>{
  if(response !== undefined && response !== null){
    var key = response.hasOwnProperty(Object.keys(response)[0]);
    if(key === true){
       var keyName =  Object.keys(response)[0];
       delete response[keyName];
    }
    delete response.cr_dt;
    delete response.up_dt;
  }
    for(const property in response){
        const isExist = pm.globals.get(outputName).hasOwnProperty(property);
        pm.expect(isExist).to.eql(true);
        if(isExist === true){
            var val = pm.globals.get(outputName);
            pm.expect(response[property]).to.eql(val[property]);
        }
    }
};
} + ';compare_obj();');