Dockerized Angular E2E测试通过本地计算机,但是Travis CI中超时

时间:2019-03-03 08:08:33

标签: angular selenium docker protractor travis-ci

我试图将我的角度e2e测试泊坞窗。简单地,我创建了一个docker-compose.test.yml文件:

version: "3.4"

services:
  # set up angular app
  angular:
    build: 
      context: ./frontend
      dockerfile: sample.Dockerfile
      target: test
    image: angular:test
    container_name: angular
    ports: 
      - "4200:4200"
    depends_on: 
      - nodejs 
    command: npm run e2e -- --protractor-config=e2e/protractor-docker.conf.js --host 0.0.0.0

  nodejs:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: development
    image: nodejs:dev
    container_name: nodejs
    ports: 
      - "3000:3000"
    networks:
      - backend
    depends_on: 
      - db
    restart: always
  # the mongo database
  db:
    image: mongo
    container_name: db
    networks: 
      - backend
    ports: 
      - "27017:27017"
networks: 
  backend: 
    driver: bridge

对于量角器的配置,我遵循了在此链接中找到的教程:

Angular Tutorial for Travis CI

我必须做出一些调整,例如:

let config = require('./protractor.conf').config;

// Tell protrator where the chrome driver is
// https://gitlab.com/dasch8/angular-ci/
// https://hub.docker.com/r/weboaks/node-karma-protractor-chrome/
config.chromeDriver = "/usr/bin/chromedriver";

config.allScriptsTimeout = 60000;

config.getPageTimeout = 60000;

config.jasmineNodeOpts.defaultTimeoutInterval = 60000;

// have it connect to the angular app
// config.baseUrl = "http://angular:4200";

config.capabilities = {
  browserName: 'chrome',
  chromeOptions: {
    args: ['--headless', '--no-sandbox', '--disable-gpu']
  },

};


exports.config = config;

使用配置,e2e测试在我的本地计算机上成功。但是,当我在.travis.yml的脚本中运行此代码时:

before_script: 
    - docker-compose -f docker-compose.test.yml build
  script:
    - docker-compose -f docker-compose.test.yml run --name angular -p 4200:4200 angular

Travis CI失败。输出为:

 - Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
  - Failed: Timed out waiting for asynchronous Angular tasks to finish after 60 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular

这很奇怪,因为我写道:

import { AppPage } from './app.po';

// Test bed
describe('workspace-project App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display navbar with title',  () => {
    page.navigateTo().then(() => {
      console.log('Successfully connected to the page');
    });

    expect(page.getHeaderText()).toEqual('hobbies');

  });
});

在Travis CI的日志中,日志打印出来:

成功连接到页面。其余日志可在此处找到:

My Travis CI logs

我不确定自己在做什么错。测试在我的本地计算机上通过很奇怪。我试图通过使用selenium / hub和selenium / node-chrome图像以不同的方式解决此问题,并使量角器连接到该selenium服务器。这是同样的故事。我可以在本地计算机上运行它,但是在TravisCI中失败。我将在另一个问题中询问硒问题。

3 个答案:

答案 0 :(得分:1)

我跟随C.Peck的回答。我在本地计算机上进行了测试,并且可以正常工作;但是,如我的日志所示,它在TravisCI中仍然不起作用:

TravisCI logs

我什至将等待超时设置为11秒,并且测试仍然失败。然后,我将allScriptsTimeout中的protractor-docker.conf.js增加到180000。这样做之后,测试通过了:

My Travis CI logs of the Successful Angular E2E Test

但是,测试需要2分10秒才能完成。这令人沮丧,因为我只是检查标题是否出现在导航栏中。恐怕如果我测试表单和单击按钮之类的东西,Travis CI可能甚至需要更长的时间。

答案 1 :(得分:1)

终于解决了!!!因此,我找到了此链接,并得到了一些启发: Chrome Not Reachable in Docker Network

幸运的是,个人使用的配置几乎相同,但是有趣的是个人被反向代理到他们的Web应用程序。

我也做同样的事情:

#default.conf for proxy service

upstream docker-web {

    #docker angular container
    server angular:4200; 
}

upstream docker-node {

    #docker nodejs container
    server nodejs:3000;
}

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://docker-web;
        #proxy_redirect off; 
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
        add_header         X-Upstream $upstream_addr;
    }

    location /api {
        proxy_pass http://docker-node;
        proxy_redirect off; 
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
        add_header         X-Upstream $upstream_addr;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

对于在nginx上托管的我的有角度的Web应用程序:

server {
    listen       4200;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }



    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

我使用我的角度图像为量角器创建了一个单独的容器,该容器将提供:

###################
##    builder    ##
###################

FROM  node:10-alpine AS builder
RUN  mkdir -p /usr/src/frontend
WORKDIR /usr/src/frontend
COPY package.json /usr/src/frontend
RUN npm cache clean --force \
  && npm install
COPY . /usr/src/frontend
RUN npm run build -- --prod


##################
##  production  ##
##################

FROM nginx:alpine AS production
COPY --from=builder /usr/src/frontend/dist/frontend /usr/share/nginx/html
COPY --from=builder /usr/src/frontend/nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

#################
## development ##
#################

# because development uses the same stuff from builder image
FROM builder AS development
EXPOSE 4200
CMD ["npm", "start"]


##################
##     test     ##
##################


# because test uses the same stuff from builder image
FROM builder AS test
RUN apk update && \
    apk upgrade && \
    apk add --no-cache chromium nss chromium-chromedriver
EXPOSE 9876
ENV CHROME_BIN /usr/bin/chromium-browser
# just needed the chromedriver!!!
# https://github.com/angular/angular-cli/issues/5019
# https://gitlab.com/dasch8/angular-ci/blob/master/README.md
CMD [ "npm", "run", "test", "--", "--no-watch", "--no-progress", "--browsers=ChromeHeadlessCI"]


FROM production

然后我也创建了硒网格服务和chrome-node服务。 Docker Selenium的文档位于:

Docker Selenium

我的错误是主机名(docker-compose文件中的容器/服务名称)未正确解析,因为它们不在同一网络上。由于量角器连接到硒,并且硒处理E2E测试,browser.get('http://proxy')无法找到有角度的应用程序,因为Selenium与代理容器不在同一网络上。然后我删除了我在docker-compose文件中使用的所有网络,并制作了一个新的docker-compose文件,其中包括我的硒网格服务(称为selenium-hub),硒/节点-chrome服务(称为chrome_node),MEAN堆栈(角度,nodejs,db)和量角器服务(通过从docker文件中定位测试版本)。

我的docker-compose.selenium.yml文件:

version: '3.4'

services:
  selenium-hub:
    image: selenium/hub:3.141.59
    container_name: selenium-hub
    environment: 
      - GRID_TIMEOUT=10000
    ports: 
      - "4444:4444"
    privileged: true
    #tty: true

  chrome_node:
    image: selenium/node-chrome:3.141.59
    volumes: 
      - /dev/shm:/dev/shm
    container_name: chrome_node
    environment:
      - HUB_PORT_4444_TCP_ADDR=selenium-hub
      - HUB_PORT_4444_TCP_PORT=4444
    ports:
      - "5555:5555"
    privileged: true
    depends_on:
      - selenium-hub
    #tty: true
  proxy: 
    build: 
      context: ./proxy
      dockerfile: Dockerfile
    image: proxy
    container_name: proxy
    # HOST:CONTAINER
    ports: 
      - "80:80"
    #tty: true
    depends_on: 
      - angular
  angular:
    build:
      context: ./frontend
      dockerfile: sample.Dockerfile
      target: production
    image: angular:prod
    container_name: angular
    ports:
      - "4200:4200"
    privileged: true 
    depends_on: 
        - nodejs
    #tty: true
  nodejs:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: development
    image: nodejs:dev
    container_name: nodejs
    ports: 
      - "3000:3000"
    depends_on: 
      - db
    restart: always
    #tty: true
  # the mongo database
  db:
    image: mongo
    container_name: db
    ports: 
      - "27017:27017"
    #tty: true
  protractor:
    build:
      context: ./frontend
      dockerfile: sample.Dockerfile
      target: test
    image: angular:test
    container_name: protractor
    ports: 
      - "9876:9876"
    privileged: true
    depends_on:
      - selenium-hub
      - chrome_node
    #tty: true --> not neccesary
    command: npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js --webdriver-update=false --dev-server-target= 

这是我的新量角器配置文件:

let config = require('./protractor.conf').config;


// do not use the chromeDriver. If you do, it will not connect to Selenium service
config.directConnect = false;


// have it connect to the angular app
config.baseUrl = "http://proxy";

// Protractor getting the page-timeout
config.getPageTimeout = 60000;

// Selenium Webdriver timeout
config.allScriptsTimeout = 60000;

// Jasmine test script timeout
config.jasmineNodeOpts.defaultTimeoutInterval = 60000;

config.seleniumAddress = 'http://selenium-hub:4444/wd/hub'

config.capabilities = {
  browserName: 'chrome',
  chromeOptions: {
    args: ['--headless', '--disable-gpu', '--no-sandbox'],
  },
};

标志--dev-server-target=让我们知道不要启动ng服务(因为我们正在nginx中启动我们的角度应用程序)。这样做之后,我的测试通过了!!!而且,我将超时设置为60000,并且测试在3秒内通过,这是一个很大的进步! :)

我在.travis.yml中使用的脚本:

before_script: 
        - docker-compose -f docker-compose.selenium.yml build
script:
        - docker-compose -f docker-compose.selenium.yml up -d chrome_node proxy
        - docker container ls
        - docker-compose -f docker-compose.selenium.yml up protractor

对于网络,docker-compose up命令由其自身处理,而Docker的DNS解析器将解析主机名(我们在docker-compose文件中的容器/服务名称)。可以在Docker的文档中找到有关Docker组成网络的更多信息:Docker Compose and Networks

这是构建的日志:

Travis CI logs

我希望这对以后的任何人都可以帮助:)

答案 2 :(得分:0)

那么,添加以下等待是否有影响?

import { AppPage } from './app.po';

// Test bed
describe('workspace-project App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display navbar with title',  () => {
    page.navigateTo().then(() => {
      console.log('Successfully connected to the page');
    });

browser.ignoreSynchronization = true

driver.wait(function () {
return driver.isElementPresent(page.getHeaderText('hobbies'));
}, timeout);

    expect(page.getHeaderText()).toEqual('hobbies');

    browser.ignoreSynchronization = false;

  });
});