Exploring the world of mocking and stubbing in protractor

I am interested in testing my angular application using protractor. The app includes an API Module that communicates with the server In these tests, I would like to mock this API Module. I am not looking to perform full integration tests, but rather tests based on user input with expected values from the API. This approach could potentially speed up client tests and allow for testing of edge cases, such as connection errors.

How can I achieve this with protractor? I am just beginning to set up integration tests.

I have utilized the npm protractor module, installed selenium, modified the default configuration, and utilized the onProtractorRunner.js to confirm my setup is functioning correctly.

What is the recommended method of mocking? My understanding is that mocking should be performed within the browser rather than directly in the test file. Since the commands in the test file are protractor-specific and sent to the selenium runners, I am unable to share JavaScript objects during the session and the test.

I anticipate that I may need to use a spy library such as sinon.js or if this functionality is already included in protractor.

Edit: I have come across this issue in the protractor issue tracker, which could potentially be a solution. Essentially, you would create a Mock Module in the test, which is then executed in the browser or the application's scope.

Edit: I have found some other promising issues. The first one discusses adding Mocks to the Angular App. The second addresses the topic of mocking the backend.

This approach seems very promising, as it would allow the Angular App to remain in its original state. However, it currently only works with the deprecated ng-scenarios.

Answer №1

This article delves into creative ways to utilize Protractor. One interesting topic it covers is the lesser-known addMockModule() function of the Protractor browser object. This function enables users to generate angular modules in Protractor (such as mocks or stubs of your API module) and deploy them to the browser to substitute the actual implementation within a specific test scenario or series of scenarios.

Answer №2

When working with Protractor tests, it's important to note that you won't have direct access to $httpBackend, controllers, or services. One workaround is to create a separate angular module and include it in the browser during the test.

  beforeEach(function(){
    var httpBackendMock = function() {
      angular.module('httpBackendMock', ['ngMockE2E', 'myApp'])
        .run(function($httpBackend) {
          $httpBackend.whenPOST('/api/packages').respond(200, {} );
        })
    }
    browser.addMockModule('httpBackendMock', httpBackendMock)
  })

Using ngMockE2E, you can simulate a backend for your application. For more detailed information on this topic, check out this informative post:

Answer №3

Even though I have not personally tested it yet, Angular offers a mock $httpBackend for end-to-end tests:

http://docs.angularjs.org/api/ngMockE2E/service/$httpBackend

Based on the information from the provided documentation, it seems possible to utilize the following setup before running your tests:

beforeEach(function() {
  $httpBackend.whenGET('/remote-url').respond(edgeCaseData);
});

Answer №4

If you're looking for mocking frameworks, I can recommend two that might be helpful to you. One is the ng-apimock and the other is json-server.

Personally, I've recently started using the ng-apimock API for mocking backend REST calls. It offers some interesting functionality through this npm library. With ng-apimock, you can define and select Scenarios and Presets, allowing you to configure which mock to use for each e2e test case. This level of control over e2e tests by providing necessary response data is quite valuable. However, the set-up process, as many online blogs suggest, is not as straightforward as it may seem. Regardless, it seems to be a suitable solution for my specific use case.

For working with this API, I had to set up a proxy.conf.json file along with defining mocks and presets, and also manage some Protractor configurations.

This API allows you to precisely configure the values to be returned from each API endpoint during e2e test runs. You can even adjust the values returned for individual e2e test cases. Additionally, there is an option called passThrough, which enables you to temporarily disable the mocks and direct calls to your real HTTP backend.

If you need more detailed information, feel free to reach out, and I can provide you with guidance on how to set it up.


UPDATE(LONG POST ALERT!!) :

Mock Server Setup (ng-apimock & protractor with express, concurrently)

mock-server.js

const express...
... 
app.listen(app.get('port'), function() {
    console.log('mock app-server running on port', app.get('port'));
});

package.json(scripts section)

...

package.json(devDependencies section)

...

mock-proxy.conf.json

...

Protractor Configuration

...

angular.json Changes

...

E2E Usage

...

The examples provided showcase the potential of mocking REST calls for E2E tests using Protractor and ng-apimock.

ng-apimock Docs

Answer №5

Looking for an efficient way to manage success and error scenarios when mocking? Check out my customizable mock module that could streamline your mocking process.

https://github.com/unDemian/protractor-mock

Answer №6

When it comes to mocking services in protractor, I have been experimenting with various approaches and finally found a solution that suits my needs. Instead of intense mocking, I focus on generating error responses since I already have a method to populate the backend through fixtures on my API server.

The key to this solution lies in using the $provide.decorator() method to tweak certain functions. Let me show you how it's implemented in the tests:

it('should mock a service', function () {
    app.mock.decorateService({
        // This snippet will cause the "login()" method of the "user" service to return a rejected promise
        // and the provided object will be used as the rejection reason.
        // The rejectPromise() is a utility function.
        user: app.mock.rejectPromise('login', { type: 'MockError' }),

        // You can modify the service behavior here
        // Warning! This code is serialized and sent to the browser
        // thus, it does not have access to Node environment
        api: function ($delegate, $q) {
            $delegate.get = function () {
                var deferred = $q.defer();
                deferred.resolve({ id: 'whatever', name: 'tess' });
                return deferred.promise;
            };
            return $delegate;
        },

        // In the background, decorateService converts the function into a string
        // You can also provide a string directly if you prefer
        // Useful for defining custom helper methods like "rejectPromise()".
        dialog: [
            "function ($delegate, $window) {",
                "$delegate.alert = $window.alert;",
                "return $delegate;",
            "}"
        ].join('\n')
    });

    // ...

    // Important!
    app.mock.clearDecorators();
});

Below is the snippet of the code:

App.prototype.mock = {
    // Make sure this is invoked before ".get()"
    decorateService: function (services) {
        var code = [
            'var decorator = angular.module("serviceDecorator", ["visitaste"]);',
            'decorator.config(function ($provide) {'
        ];

        for (var service in services) {
            var fn = services[service];

            if (_.isFunction(fn)) {
                code.push('$provide.decorator("'+ service +'", '+ String(fn) +');');
            } else if (_.isString(fn)) {
                code.push('$provide.decorator("'+ service +'", '+ fn +');');
            }
        }

        code.push('});');

        browser.addMockModule('serviceDecorator', code.join('\n'));
    },
    clearDecorators: function () {
        browser.clearMockModules();
    },
    rejectPromise: function (method, error, delay) {
        return [
            'function ($delegate, $q) {',
                '$delegate.'+ method +' = function () {',
                    'var deferred = $q.defer();',
                    '',
                    'setTimeout(function () {',
                        'deferred.reject('+ JSON.stringify(error) +');',
                    '}, '+ (delay || 200) +');',
                    '',
                    'return deferred.promise;',
                '};',
                '',
                'return $delegate;',
            '}'
        ].join('\n');
    }
};

Answer №7

Utilizing protractor to conduct end-to-end tests serves the purpose of verifying the smooth integration of the application. When it comes to testing individual UI components separately, it is more efficient to incorporate small elements into regular tests, much like AngularJS tests its directives.

However, for those who are intent on mocking, one approach is to generate a distinct version of the application with placeholders replacing real services.

Answer №8

Check out these additional options for simulating an HTTP server:

  • MockServer is a versatile mock server that supports various platforms like Java, .Net, and Node. It requires installation and self-hosting.
  • JSONPlaceholder is a cloud-based service for creating fake APIs. It also offers API documentation generation features.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I would like for this message to appear periodically following the initial execution

I have developed a unique welcome feature using HTML and CSS that I would like to showcase intermittently. --------------------------- My Desired Outcome --------------------------- Initially, this feature should be triggered once (when a user first acce ...

Unable to export Interface in Typescript - the specific module does not offer an export named Settings

I am encountering an issue while trying to export/import an interface in Typescript. The error message I receive is causing confusion as I'm unsure of where I went wrong. Uncaught SyntaxError: The requested module '/src/types/settings.ts' ...

AngularJS does not clear the array after a POST request is made

ISSUE Greetings! I am encountering an odd behavior with my backend. When I submit data, everything works flawlessly. However, if I press ENTER and submit an empty field, it reposts the previous value. Initially, I cannot submit an empty field, but after e ...

Utilize separate environment variables for distinct environments within a React project

Is there a secure method to externalize all environment variables, including secret keys, for a React application within a DevOps setup? Our goal is to streamline the build process across different environments, each with its own unique set of environment ...

Error message: NodeJS express unknown function or method get()

Currently, I am facing an issue while working with express and Pug (Jade) to render a page as the get() function is returning as undefined along with warnings... I followed these steps: npm install express --save npm install pug --save Here's a sn ...

Instructions for including packages in .vue files

Within the script section of my .vue file, I have the following code: <script> import get from 'lodash.get'; ... </script> Despite trying to import lodash.get, I keep encountering an error message stating ReferenceError: ge ...

Setting up Selenium for Python in Eclipse: A complete guide

While I am aware that we need to utilize the pip install command for installing Selenium, I am curious about how Eclipse incorporates those package files into a Python project. In Java, we typically add jar files manually to the build path. How does the ...

Using Next Js for Google authentication with Strapi CMS

Recently, I've been working on implementing Google authentication in my Next.js and Strapi application. However, every time I attempt to do so, I encounter the following error: Error: This action with HTTP GET is not supported by NextAuth.js. The i ...

Incorporating D3.js into Angular 6 for interactive click events

Currently working on building a visual representation of a tree/hierarchy data structure using d3.js v4 within an Angular environment. I've taken inspiration from this particular implementation https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5e ...

The nested route controller in ui-router is not functioning correctly

PLNKR: http://plnkr.co/edit/Or4JTiUrPOJUoW78c8Xd Hello everyone, currently facing an issue that I have managed to simplify into the following example: var myapp = angular.module('myapp', ["ui.router"]) myapp.config(function($stateProvider, $url ...

Connecting to a MongoDB or Mongoose database dynamically using Node.js

I am currently working on developing a multi-tenant application where each client will have its own dedicated database. Here is my scenario: I have created a middleware that identifies the client based on their subdomain and retrieves the necessary datab ...

As soon as I open Gmail_login using selenium, I notice that I am not already signed in. Upon attempting to sign in, a message pops up with

"The security of this browser or app may be compromised. For more information, consider switching to a different browser. If you are already using a supported browser, attempt to refresh your screen and try logging in again." Is there a method f ...

`My jquery mobile application fails to trigger the pageinit or ready events`

My website consists of 3 PHP pages: one index page and two subpages for sales and products. The index page has links to these subpages. When I click on the sales link, it is supposed to load sales data either on pageinit or document ready. However, no code ...

The object continues to be undefined despite its placement in a global scope

Currently, I am working on building a game of pong and encountering an issue with creating a paddle in the initialize method. Despite storing it as a global variable, the paddle remains undefined. I even tried making it a property of 'window', bu ...

Implement ajax functionality to update an object within a JSP page

On my JSP page, I have implemented an accordion list (utilizing Bootstrap 3) with text and a Delete button within each node. When the user clicks on the delete button, that specific list item is removed. To construct the accordion list, I bring in an Array ...

Google Maps introduces a new feature that creates a blur effect on

Currently, I am utilizing the sample code provided below to superimpose an element on a Google map. <!DOCTYPE HTML> <html> <head> <title>Google Map Test</title> <style> html, body { margin: 0; ...

What is the approach for capturing system or website sound using JavaScript?

After creating an online drumkit and guitar player, I am now looking to enhance my website by adding a feature that allows users to record their sessions and download them. Since I am new to web development and familiar only with HTML, CSS, JavaScript, and ...

Oops! SAPUI5 is encountering an issue with reading property '0' of undefined

Is there a possibility of encountering multiple errors leading to this specific error message? https://i.stack.imgur.com/RpWhw.png Despite searching online, it appears that the error occurs in the JavaScript file when getelementbyid returns null. However ...

Modifying the value of a local variable results in a corresponding adjustment to the original global variable's value

Within my Node.js program, I have set up an array named "list" in the routes section. This array is populated with values from a function defined in the model. The code for this route looks like: var express = require('express'); var router = ex ...

Troubleshooting: Blank screens displayed in nested ui-views in Angular ui-router

I have scoured through various resources but have yet to find a solution to my issue. On my top page, I have a ui-view followed by a works page with a second ui-view. I tried adding ui-view="portfolio" and linking it in my main js file, but nothing seems t ...