Angular – Testing and Debugging with Protractor

×

Debugging Protractor Tests

Prepare your Protractor Code

  • Add debugger keyword in your protractor code – Code execution will stop here !
  fit('should run the Global Goals View and test Vacation Semester Feature', () => {
    const waitTimeout = 400;
    waitByIdAndClick('nav-global-goals-button');
    validateHeadline('Meine Ziele');
    debugger;
    ...

Setup Debug Session

Action Action Details
Start Protractor with —inspect-brk switch $ node –inspect-brk node_modules/protractor/bin/protractor e2e/protractor.conf.js
  • Run Chrome Brower with URL: chrome://inspect/#devices
  • Press Inspect Button to start Debug Session
protractor_img1.jpg NOT Found
  • Debugger gets started and stops execution before running ANY Protractor Tests
protractor_img2.jpg NOT Found
  • Press F8 to stop code execution on loacation marked by debugger keyword
protractor_img3.jpg NOT Found

Reference

Helper Function for running Protractor Tests

Calling Ajax to reset data before each run

import {HttpClient} from 'protractor-http-client';
..
 beforeEach(() => {
    const http = new HttpClient('http://localhost:8080/');
    http.failOnError = true;
    const resetResponse = http.get('/pythia/api/data/resetdata/test3');
    expect(resetResponse.statusCode).toEqual(200);
...

Scroll Down Browser Window

 const scrollDown = function () {
    browser.executeScript('window.scrollTo(0,document.body.scrollHeight);').then(function () {
      browser.sleep(500);
    });
  };

Closing an External Browser Window

  const closeBrowserWindow = function () {
    // Close the external browser window
    browser.getAllWindowHandles().then((handles) => {
      browser.driver.switchTo().window(handles[1]);
      browser.driver.close();
      browser.driver.switchTo().window(handles[0]);
    });
  };

Angular Services

Providing a singleton service

Two ways to make a service singleton in Angular

  • Declare that the service should be provided in the application root.
  • Include the service in the AppModule or in a module that is only imported by the AppModule.

Beginning with Angular 6.0, the preferred way to create a singleton services is to specify on the service that it should be provided in the application root. This is done by setting providedIn to root on the service’s @Injectable decorator:

Prefered Angular 6 Way using @Injectable decorator

  • Angular creates a single, shared instance of service and injects into any class that asks for it.
  • Registering the provider in the @Injectable metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class VoteService {
}

Register Service in providers array of @NgModule

import {VoteService} from './vote.service';
...
@NgModule({
  imports:      [ BrowserModule],
  declarations: [ AppComponent],
  bootstrap:    [ AppComponent],
  providers: [VoteService]
})

Reference

Angular 6: Using Karma for Unit Testing

×

Karma Installation

Install Karma Client on Windows 10 an run it from Command Line

D:\dev\myTestProjects\dobby-the-companion>  npm install -g karma-cli
C:\Users\Helmut\AppData\Roaming\npm\karma -> C:\Users\Helmut\AppData\Roaming\npm\node_modules\karma-cli\bin\karma
+ karma-cli@1.0.1
updated 1 package in 0.421s

D:\dev\myTestProjects\dobby-the-companion>  karma start
08 08 2018 16:03:24.227:WARN [karma]: No captured browser, open http://localhost:9876/
08 08 2018 16:03:24.232:WARN [karma]: Port 9876 in use
08 08 2018 16:03:24.233:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9877/

Run all Karma test

D:\dev\myTestProjects\dobby-the-companion>  ng test
 10% building modules 1/1 modules 0 active(node:25148) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
10 08 2018 14:07:37.807:WARN [karma]: No captured browser, open http://localhost:9876/
10 08 2018 14:07:37.813:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
10 08 2018 14:07:37.813:INFO [launcher]: Launching browser Chrome with unlimited concurrency
10 08 2018 14:07:37.817:INFO [launcher]: Starting browser Chrome              10 08 2018 14:07:43.581:WARN [karma]: No captured browser, open http://localhost:9876/

Missing Error Messages with Angular / Karma testing – add source-map=false parameter

D:\dev\myTestProjects\dobby-the-companion>   ng test --source-map=false
 10% building modules 1/1 modules 0 active(node:6332) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
10 08 2018 14:24:47.487:WARN [karma]: No captured browser, open http://localhost:9876/
10 08 2018 14:24:47.492:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
10 08 2018 14:24:47.492:INFO [launcher]: Launching browser Chrome with unlimited concurrency
10 08 2018 14:24:47.504:INFO [launcher]: Starting browser Chrome              10 08 2018 14:24:50.370:WARN [karma]: No captured browser, open http://localhost:9876/
10 08 2018 14:24:50.748:INFO [Chrome 67.0.3396 (Windows 10.0.0)]: Connected on socket Ml0r-gsA3JNw1F0sAAAA with id 52984303

Run ONLY a single Karma test

Change in your IDE :   
           describe('LoginComponent', () => {
        -> fdescribe('LoginComponent', () => {   

Reference

Using RouterTestingModule to test Angular Router Object

Code to be tested

import {Router} from '@angular/router';

export class myComponent  {
  constructor(   private router: Router ) { }           
  triggerMessageAction(m: Message) {
   
    if (m.messageClass === 'internal') {
      this.router.navigate([m.messageLink]);
    } 
  }

Karma/Jasmine Test Code

import {RouterTestingModule} from '@angular/router/testing';
import {Router} from '@angular/router';

describe('WarningDialogComponent', () => {
  
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      ...
      imports: [ RouterTestingModule ],     
    }).compileComponents();
  }));

  beforeEach(inject([MessagesService], (service: MessagesService) => {
   ...
  }));

  it('should close Warning Dialog after pressing on a Warning Action ', inject([MessagesService, Router],
    (service: MessagesService, router: Router) => {
      spyOn(component, 'triggerMessageAction').and.callThrough();
      spyOn(router, 'navigate').and.returnValue(true);
      expect(component.triggerMessageAction).toHaveBeenCalledWith(jasmine.objectContaining( {messageType: 'logout'}));
      expect(router.navigate).toHaveBeenCalledWith('login'); 
    }));
});

Potential Error

Error:  : Expected a spy, but got Function.
Usage: expect().toHaveBeenCalledWith(...arguments)

Fix: Add spyOn for the methode you want to spy
spyOn(router, 'navigate').and.returnValue(true)

Using SpyOn to run UNIT-tests for Angular Router Calls

  • spyOn() takes two parameters: the first parameter is the name of the object and the second parameter is the name of the method to be spied upon
  • It replaces the spied method with a stub, and does not actually execute the real method
  • The spyOn() function can however be called only on existing methods.
  • A spy only exists in the describe or it block in which it is defined, and will be removed after each spec

Sample Code

 it(`Fake Login with should set isAuthenticated Flag and finally call route: ects`,
    async(inject([AuthenticationService, HttpTestingController],
    (service: AuthenticationService, backend: HttpTestingController) => {

      spyOn(service.router, 'navigate').and.returnValue(true);

      service.login('Helmut', 'mySecret');
      // Fake a HTTP response with a Bearer Token
      // Ask the HTTP mock to return some fake data using the flush method:
      backend.match({
        method: 'POST'
      })[0].flush({ Bearer: 'A Bearer Token: XXXXXXX' });

      expect(service.router.navigate).toHaveBeenCalled();
      expect(service.router.navigate).toHaveBeenCalledWith(['ects']);
      expect(service.isAuthenticated()).toEqual(true);
    })));

Reference

My Fist Karma Sample

Screenshot Chrome Browser after running Tests Details
Image karma_img1.jpg NOT Found
  • Karma verison is v1.7.1
  • Only 1 of our 18 test was running as we use fdescribe
  • The test shows no error
  • The Screen is devided into 2 divs
  • Div with class html-report shows the test details
  • Div with class root0 shows the angular HTML components
Html Code Type Script TestCode: login.components.spec.ts
</p> <div class="text-center"> <h1>TestKarma</h1> <p> <img class="img-fluid" src="assets/th-logo.jpeg"> </div> <form (ngSubmit)="onLogin(f)" #f="ngForm"> <div class="form-group"> <label for="username">Benutzer</label><br /> <input type="text" id="username" name="username" ngModel class="form-control"> </div> <div class="form-group"> <label for="password">Passwort</label><br /> <input type="password" id="password" name="password" ngModel class="form-control"> </div> <p> <button class="btn btn-primary" type="submit">Anmelden</button><br /> </form

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LoginComponent } from './login.component';
import {FormsModule} from '@angular/forms';
import {RouterTestingModule} from '@angular/router/testing';
import {AuthenticationService} from '../authentication.service';
import {AuthGuard} from '../auth-guard.service';

fdescribe ('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let h1:        HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [FormsModule, RouterTestingModule ],
      providers: [AuthenticationService]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    h1 = fixture.nativeElement.querySelector('h1');
    console.log(h1);

    fixture.detectChanges();
  });

  it('should have <h1> with  TestKarma', () => {
    const loginElement: HTMLElement = fixture.nativeElement;
     h1 = loginElement.querySelector('h1');
    expect(h1.textContent).toEqual('TestKarma');
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Debug Karma Test

  • Add debugger keyword to your Java Script Code you want to debug
  • Start Karma tests with : ng test
  • Add Chrome Debugger Tools by pressing F12
  • Reload your page – Javascript code should stop on the line with debugger statement
Screenshot Chrome Browser after running Tests Details
Image karma_img3.jpg NOT Found Image karma_img3.jpg NOT Found

A more complexer Sample setting up a Karma Test Environment for Integration Tests

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CurrentSemesterComponent } from './current-semester.component';
import { CommonHeaderComponent } from '../common-header/common-header.component';
import { NavigationComponent } from '../navigation/navigation.component';
import { RouterTestingModule } from '@angular/router/testing';
import { MatIconModule } from '@angular/material';
import {MatButtonToggleModule} from '@angular/material/button-toggle';
import {AuthenticationService} from '../auth/authentication.service';


fdescribe('CurrentSemesterComponent', () => {
  let component: CurrentSemesterComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ CurrentSemesterComponent,CommonHeaderComponent, NavigationComponent ],
      providers: [AuthenticationService,],
      imports: [RouterTestingModule, MatIconModule, MatButtonToggleModule ],
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CurrentSemesterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Jasmine Test Order

  • Currently (v2.x) Jasmine runs tests in the order they are defined
  • However, there is a new (Oct 2015) option to run specs in a random order, which is still off by default
  • According to the project owner, in Jasmine 3.x it will be converted to be the default.

Reference

Jasmine and Timeout

Reference

https://makandracards.com/makandra/32477-testing-settimeout-and-setinterval-with-jasmine

Error: router-outlet’ is not a known element:

Error Details

'router-outlet' is not a known element:
1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. 

"<app-navigation></app-navigation>
<div class="container">
  [ERROR ->]<router-outlet></router-outlet>
</div>
"): ng:///DynamicTestModule/AppComponent.html@2:2

Fix – import RouterTestingModule

import {RouterTestingModule} from '@angular/router/testing'

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      imports: [ RouterTestingModule ]
    }).compileComponents();
  }));

Reference

Error: app-navigation’ is not a known element:

Error Details

AppComponent should create the app
Failed: Template parse errors:
'app-navigation' is not a known element:
1. If 'app-navigation' is an Angular component, then verify that it is part of this module.
2. If 'app-navigation' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("

<title>app</title>
[ERROR ->]<app-navigation></app-navigation>


Angular code

app.component.html 

<app-navigation></app-navigation>
<div class="container">
  <router-outlet></router-outlet>
</div>


navigation.component.ts 
import {Component, OnInit} from '@angular/core';
import {AuthenticationService} from '../auth/authentication.service';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
...

Fix – Add a stubbing Object

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import {RouterTestingModule} from '@angular/router/testing'
import {Component} from '@angular/core';

@Component({selector: 'app-navigation', template: ''})
class NavigationStubComponent {}

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        NavigationStubComponent
      ],
      imports: [ RouterTestingModule ]
    }).compileComponents();
  })

Reference

Error Injecting a service

Error Datails: NullInjectorError: No provider for AuthenticationService!

NavigationComponent should create
Error: StaticInjectorError(DynamicTestModule)[NavigationComponent -> AuthenticationService]: 
  StaticInjectorError(Platform: core)[NavigationComponent -> AuthenticationService]: 
    NullInjectorError: No provider for AuthenticationService!

Fix add AuthenticationService to the provider Array

import {AuthenticationService} from '../auth/authentication.service';

describe('NavigationComponent', () => {
  let component: NavigationComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ NavigationComponent ],
      providers: [
        MatSnackBar,
        Overlay,
        AuthenticationService,
        SnackBarComponent,
        DatePipe],
    })
    .compileComponents();
  }));

Error: Failed: Template parse errors

Error Datails: There is no directive with “exportAs” set to “ngForm”

LoginComponent should create
Failed: Template parse errors:
There is no directive with "exportAs" set to "ngForm" ("

<form (ngSubmit)="onLogin(f)" [ERROR ->]#f="ngForm">
  <div class="form-group">
    <label for="username">Benutzer</label>
"): ng:///DynamicTestModule/LoginComponent.html@5:30

Fix: Add FormsModule Import

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [FormsModule ],
      providers: [AuthenticationService]
    })

Reference

Error: StaticInjectorError(DynamicTestModule)[AuthGuard -> Router]:

Error Datails: NullInjectorError: No provider for Router!

Error: StaticInjectorError(DynamicTestModule)[AuthGuard -> Router]: 
  StaticInjectorError(Platform: core)[AuthGuard -> Router]: 
    NullInjectorError: No provider for Router!
Error: StaticInjectorError(DynamicTestModule)[AuthGuard -> Router]: 
  StaticInjectorError(Platform: core)[AuthGuard -> Router]: 
    NullInjectorError: No provider for Router

Fix: Add RouterTesting Module

import { TestBed, async, inject } from '@angular/core/testing';
import { AuthGuard } from './auth-guard.service';
import {AuthenticationService} from '../auth/authentication.service';

import {RouterTestingModule} from '@angular/router/testing';
describe('AuthGuard', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [AuthGuard,
         AuthenticationService,
      ],
      imports: [
        RouterTestingModule
      ],
    });
  });

Setup for NGXLogger

Note: the setup below should use a single logger instance for both : Code Base and for Karma/Jasmine Tests!

import { TestBed, inject } from '@angular/core/testing';
import {LoggerConfig, LoggerTestingModule, NGXLogger} from 'ngx-logger';

fdescribe('InterceptService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        LoggerTestingModule
      ],
      providers: [   NGXLogger, LoggerConfig, 
       ... ]
    });
  });

fit(' should create and test NGXLogger  !',
    inject([NGXLogger], (logger: NGXLogger) => {
        // config the Logger
      logger.updateConfig({ level: NgxLoggerLevel.DEBUG });
        // send a Test message from our Karma Code to the
      logger.log('Logger Message from Karma Testing Module !');
        // Override the logger instance from our production code
      component.logger = logger;
        // Invoke a function to test  NGXLogger functionality works for our Code Base too !
      component.syncSemsterData();
      expect(component).toBeTruthy();
    }));
});

Logger Output : 
2019-08-13T11:37:46.298Z LOG [semester-goal-setting.component.spec.ts:95] Logger Message from Karma Testing Module !
2019-08-13T11:37:46.743Z LOG [semester-goal-setting.component.ts:130] syncSemsterData():: Subjects: 3 - all Marks Valid : true - saveButtonIsDisabled false

A complexer Sample mocking Services and Components

@Component({selector: 'app-semester-header', template: ''})
class SemesterHeaderStubComponent {}

@Component({selector: 'app-navigation', template: ''})
class NavigationStubComponent {}

export class MockDataService {
  public isReady = true;
  getIsReady(): boolean {
    return this.isReady;
  }
  setAppStatusFailed(): void  {}

  getSemesterById(): Semester {
  return new Semester(1, 20, 30 , false, true,
    [ new Module('Mathematik', 8, 2.2, 2.0, 3.0, true),
               new Module('JAVA Programming 2', 7, 2.2, 2.0, 3.0, true),
    ]);
  }
}

describe('CurrentSemesterComponent', () => {
  let component: CurrentSemesterComponent;
  let fixture: ComponentFixture;
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ CurrentSemesterComponent, SemesterHeaderStubComponent, NavigationStubComponent],
      imports: [
        LoggerTestingModule,
        RouterTestingModule,
        NoopAnimationsModule
      ],
      schemas:  [NO_ERRORS_SCHEMA],
      providers: [
        NGXLogger,
        LoggerConfig,
        {provide: DataService, useValue: new MockDataService() } ]
    });
    fixture = TestBed.createComponent(CurrentSemesterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  /*
      Whenever dataService is not ready  setAppStatusFailed() should be called with systemError param
   */
  it('if dataService not READY setAppStatusFailed() should be called with param \'systemError\' ', () => {
    let dataService: MockDataService;
    dataService = TestBed.get(DataService);
    const spy = spyOn(dataService, 'setAppStatusFailed');
    dataService.isReady = false;
    component.ngOnInit();
    expect(component).toBeTruthy();
    expect(dataService.setAppStatusFailed).toHaveBeenCalledWith('systemError');
  });
  /*
        Whenever dataService is READY setAppStatusFailed() should NOT be called anyway
     */
  it('if dataService is READY setAppStatusFailed() should not have been called', () => {
    let dataService: MockDataService;
    dataService = TestBed.get(DataService);
    const spy = spyOn(dataService, 'setAppStatusFailed');
    dataService.isReady = true;
    component.ngOnInit();
    expect(component).toBeTruthy();
    expect(dataService.setAppStatusFailed).not.toHaveBeenCalled();
  });

});

Mock a Service Object

export class MockDataService {
  public isReady = true;

  getSemesterById(semId: number): Semester {
    return new Semester(  '20192' , 30, 0,  '', 0,
      [
        new Mark( 4.5, 5, new Exam ( '6', 'Mathematik S1'), 3, true ),
        new Mark( 2.5, 7, new Exam ( '5', 'JAVA Programming II S1'), 2, true ),
        new Mark( 1.5, 8, new Exam ( '4', 'C ++ Programming II S1'), 2.7, true ),
      ])
  }

}

fdescribe('SemesterGoalSettingComponent', () => {
  let component: SemesterGoalSettingComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SemesterGoalSettingComponent ],
      schemas: [NO_ERRORS_SCHEMA],
      imports: [ MatTableModule, MatDialogModule, RouterTestingModule, LoggerTestingModule ,  HttpClientModule,
        HttpClientTestingModule, MatSnackBarModule],
      providers: [ NGXLogger, LoggerConfig, MatSnackBar, MatDialog,
        { provide: DataService, useValue: new MockDataService() } ]
    })
    .compileComponents();
  }));
...

Simulate a Button Press Action

Karma/Jasmine Test Code:
beforeEach(() => {
    fixture = TestBed.createComponent(SemesterHeaderComponent);
    component = fixture.componentInstance;
    // 'autoDetectChanges' will continually check for changes until the test is complete.
    // This is slower, but necessary for certain UI changes as changes triggered by ngFor Ops
    fixture.autoDetectChanges(true);
  });

it('Button Press should redirect to Central ECTS Page  ', () => {
    spyOn(component, 'navigateTop');
    const el1 =   fixture.debugElement.query(By.css('#return-to-top-level-page')).nativeElement;
    el1.click();
    expect(component.navigateTop).toHaveBeenCalled();
  });

HTML Code:

    <button id="return-to-top-level-page" class="mat-button-study-progress" mat-button  (click)="navigateTop()" > <mat-icon class="button-navigate-back color_white" >arrow_back_ios</mat-icon> </button>


JavaScript Code:

 navigateTop() {
    this.logger.warn('SemesterHeaderComponent::navigate  to ECTS Page !: ');
    this.router.navigate(['ects']);
  }

JUNIT and Java

×

Overview Versions

  • Netbeans 8.2
  • JUNIT 4
  • JUNIT uses Java Reflection API to run Object Methods

Java IO sample

package fileio;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FileIO {
    private static final Logger LOGGER = Logger.getLogger( 
            Thread.currentThread().getStackTrace()[0].getClassName() );
    
    /** FileOutputStream Descriptor */        
    private FileOutputStream fos = null;
    /** OS-FileName */
    final private String fileName ;
    public FileOutputStream getFileOutputStream() { return fos; }
    public String getFileName() { return fileName; }
    
    public static void main(String[] args) {
        String writeContent = "This is my Data which needs" +
	        " to be written into the file";
        String fileName = "C:/myfile.txt";
        //LOGGER.setLevel(Level.FINEST);
        //LOGGER.setLevel(Level.SEVERE);              
        LOGGER.setLevel(Level.INFO);                  // Setting current Logger Level
        
        Locale.setDefault(new Locale("en", "EN"));  // Change Logger Language to English 
        FileIO fileIO = new FileIO(fileName);
        fileIO.doIO(fileName, writeContent);
    }
       
    public FileIO(String fileName) {
        this.fileName = fileName;
        LOGGER.info("FileIO constructor for fileName: " + fileName);
    }
/**
 * Opens, writes and closes FileOutputStream fos.
 * 

* If open was successfull both fileName and FileOutputStream Object are * stored in the related class attributes fileName, fos for later access * @param fName The fileName at OS Level * @param writeContent The message to be written to our FileOutputStream * @return true If write was successfull else return false */ public boolean doIO(String fName,String writeContent) { try { openStream(fName); // Got an excpetion if open failse int bytesWritten = writeToStream( writeContent); boolean ret = closeStream(); String loggerString = new StringBuilder("Byte written to File ") .append( this.fileName ).append(": " ).append(bytesWritten).toString(); LOGGER.info(loggerString); return true; } catch ( IOException ioe) { // Only log the Exception Message - stacktrace should be printed by low level // Exception handler. Close the stream when there is am Exception ! LOGGER.log( Level.SEVERE, ioe.toString() ); return false; } finally{ closeStream(); } } /** * Opens a FileOutputStream fos. *

* If open was successfull both fileName and FileOutputStream Object are * stored in the related class attributes fileName, fos for later access * @param fileName The fileName at OS Level * @throws IOException If stream can't be opened at OS level * @return true If Stream was opened sucessfully */ public boolean openStream (String fileName) throws IOException { String fName = fileName; FileOutputStream lfos; File file; try { if ( fName == null ) { throw new IOException("fileName can't be null"); } //Specify the file path here file = new File(fileName); lfos = new FileOutputStream(file); /* This logic will check whether the file exists or not. If the file * is not found at the specified location it would createa new file */ if (!file.exists()) { file.createNewFile(); } this.fos = lfos; // save the FileOutputStream Descriptor for later usage return true; } catch ( IOException ioe) { String errorMesg = "Error open File-Stream " + fileName; LOGGER.log( Level.SEVERE, ioe.toString(), ioe ); this.fos = null; throw new IOException(errorMesg); } } /** * Writes a String Message to already opened FileOutputStream fos. *

* Note: This methode will convert a potenial NullPointerExceptions tiggered * by either message=null or FileOutputStream fos=null to an IOExeption. * @param message The String to be written to the Stream Descriptor fos * @throws IOException If stream is not already opened at OS level or If messsage is null * @return byteCnt The number of bytes written to our opened Stream */ public int writeToStream( String message) throws IOException { try { /*String content cannot be directly written into * a file. It needs to be converted into bytes */ if (message == null ) { throw new IOException("Message should NOT be null ! "); } // don't need to check for null ! if (!(fos instanceof FileOutputStream)) { throw new IOException("FileOutputStream fos should NOT be null ! "); } byte[] bytesArray = message.getBytes(); int byteCnt = bytesArray.length; fos.write(bytesArray); fos.flush(); String loggerString = new StringBuilder("Successfully written " ) .append( byteCnt ).append(" Bytes to File: ").append(fileName).toString(); LOGGER.info(loggerString); return byteCnt; } catch (IOException ioe) { String errorMesg = "Error writing to File: " + fileName + "\n " + ioe.toString(); LOGGER.log( Level.SEVERE, ioe.toString(), ioe ); throw new IOException(errorMesg); } } /** * Closes FileOutputStream fos. *

* Note: Closing an alreday closed FileOutputStream will not throw any error * Even we get an error during close operation closeStream() methode will only log an error * without re-throwing the underlying Exception. * @return true if Stream was closed successfully */ public boolean closeStream() { try { if (fos != null) { fos.close(); } fos = null; return true; } catch (IOException ioe) { String errorMesg = "Error closing Stream " + fileName; LOGGER.log( Level.SEVERE, ioe.toString(), ioe ); return false; } } }

JUnit test for above IO sample

  • Create JUNIT test with Netbeans 8.2: Right Click on Java File -> Tools -> Create/Update Tests


    @AfterClass
    public static void tearDownClass() {
    }
    
    @Before
    public void setUp() {
         String fName = "c:/myFileTest";
        instance = new FileIO(fName);
    }
    
    @After
    public void tearDown() {
    }

    /**
     * Test of main method, of class FileIO.
     */
    @Test
    public void testMain() {
        System.out.println("-> Start Test main()");
        String[] args = null;
        FileIO.main(args);
        System.out.println("   Test main() OK");
    }

    /**
     * Test of openStream method, of class FileIO.
     */
    @Test
    public void testOpenStream() {
        System.out.println("-> Start Test openStream()");
        String fName = "c:/myFileTest";
        boolean result = false;
        // FileIO instance = new FileIO();
        boolean expResult = true;
        try {
            result = instance.openStream(fName);
        } catch ( IOException iex) {}
        
        assertTrue("Expected Test Result " + expResult +  " - Got: " +  result,  expResult==result);
        System.out.println("   Test openStream() OK!");
    }
    
       /*
        *  Open a Non-Existing File - Should fail with an IOException    
        */
    @Test (expected=IOException.class)
    public void testOpenStreamFailed() throws IOException{
        System.out.println("-> Start Test testOpenStreamFailed() - Fails with an IOException !");
        String fName = "X:/myFileTestFailed";
        boolean result = false;
        FileOutputStream expResult = null;
        result = instance.openStream(fName);
    }
    
    /**
     * Test of writeToStream method, of class FileIO.
     */
    @Test 
    public void testWriteToStream() {
        String fName = "c:/myFileTest";
        FileOutputStream fos;
        int bytesWritten = 0;
        System.out.println("-> Start Test writeToStream()");
        String message = "This is just a Test";
        int bytesToBeWritten = message.getBytes().length ;
        try {
            instance.openStream(fName);
            bytesWritten = instance.writeToStream(message);
        } catch ( IOException iex) {}
        assertTrue("Error::  Expected bytesToBeWritten: " +  bytesToBeWritten 
                + " - GOT bytesWritten " + bytesWritten ,bytesWritten == bytesToBeWritten );
        System.out.println("   Test writeToStream() OK! - bytesWritten: " + bytesWritten);

    }

    /*
        In this test we simulate a write to a Not initialized Stream Desriptor
        This Test should fail with an IOExecption     
    */
    @Test (expected=IOException.class)
    public void testWriteToStreamFailed() throws IOException {
        String fName = "c:/myFileTest";
        FileOutputStream fos = null;

        System.out.println("-> Start testWriteToStreamFailed() - Fails with IOException !");
        String message = "This is just a Test";
        int bytesToBeWritten = message.getBytes().length ;
        // instance.writeToStream should trigger an IOException
        int bytesWritten = instance.writeToStream( message);
    }
    
    /**
     * Test of closeStream method, of class FileIO.
     * We also test that closing a closed Stream doesn't throw any Exception lile in socket opes
     */
    @Test
    public void testCloseStream() {
        String fName = "c:/myFileTest";
        FileOutputStream fos = null;
        System.out.println("-> Start Test closeStream()");
        String message = "This is just a Test";
        boolean result = false;
         
        // FileIO instance = new FileIO();
        try {
            instance.openStream(fName);
            result = instance.closeStream();
          } catch ( IOException iex) {}
        assertTrue("Error: Expected true in first close !", result);
            // Now closing an alreay closed Stream
        result = instance.closeStream();
        assertTrue("Error: Expected true in 2.nd close !", result);
        System.out.println("   Test closeStream() - 2.nd Close OK No Exception !" +
            " - ret Status: " + result );
    }

    /**
     * Test of doIO method, of class FileIO.
     * Should Work and return true !
     */
    @Test
    public void testDoIO() {
        System.out.println("-> Start Test doIO()");
        String fName = "c:/myFileTest";
         String writeContent = "This is my Data which needs" +
	        " to be written into the file";
        //FileIO instance = new FileIO();
        boolean result = instance.doIO(fName,writeContent);
        assertTrue("Error: Expected true !", result);
        System.out.println("   Test doIO() OK - result " + result );
    }    
    
    /**
     * Test of doIO method, of class FileIO. 
     * This test is the most important ONE as we will test that a failed Write closes fos 
     * descriptor to avoid a resource leak. To test this does we issue an add. write after calling 
     * doIO(). This add write should fail indicating that fos descriptor was already closed
     * TestString : null
     *              -> doIO() should return false but close fos
     *              -> An addition Write Test using the closed fos shoud fail too   
     */

    @Test
    public void testDoIOFailed() throws IOException {
        System.out.println("-> Start Test testDoIOFailed() - Fails with an Assert !");
        String fName = "c:/myFileTest";
        String writeContent = null;
        //FileIO instance = new FileIO();
        boolean result = instance.doIO(fName,writeContent);
        assertFalse("Error: Expected false !", result);
            // As we can get the Stream closed status form FileOutputStream object we start a test
            // write. Note: 
            // After a failure the Stream should be closed - we expect the the next write to fail !
        writeContent = "This WRITE should NOT Work";
        int bytesToBeWritten = writeContent.getBytes().length;
        int bytesWritten = -1;
        try {
            bytesWritten = instance.writeToStream( writeContent);
        } catch ( IOException e) {}
        assertFalse("This Write should fail - as the FOS descpriptor should be closed before!", bytesToBeWritten == bytesWritten );
    }    
    
    /* 
        Writing a null Message should reurn an IOException and Not a java.lang.NullPointerException 
    */
    @Test
    public void testWriteFailed() throws IOException{
        String fName = "c:/myFileTest";
        FileOutputStream fd;
        String message = null;
        int bytesToBeWritten = -1;
        int bytesWritten = -1;
        System.out.println("-> Start Test testWriteFailed() - Catches IOException !");
       
        instance.openStream(fName);
        fd = instance.getFileOutputStream();
        assertTrue(fd instanceof FileOutputStream);
     
        try {
            bytesWritten = instance.writeToStream( message);
        } catch ( IOException iex) {} 
        
        //  This Test raises an java.io.IOException: Message should NOT be null at all -
        // > bytesWritten remain unchanged 
        assertTrue("Error::  Expected bytesToBeWritten: " +  bytesToBeWritten 
                + " - GOT bytesWritten " + bytesWritten ,bytesWritten == bytesToBeWritten );
 
        boolean closeResult = instance.closeStream();
        assertTrue("Error: Expected true !", closeResult);       
        System.out.println("   Test testWriteFailed()  OK "  );
    }    
}

JUNIT Test Output

-> Start Test writeToStream()
   Test writeToStream() OK! - bytesWritten: 19
-> Start testWriteToStreamFailed() - Fails with IOException !
-> Start Test doIO()
   Test doIO() OK - result true
-> Start Test main()
   Test main() OK
-> Start Test closeStream()
   Test closeStream() - 2.nd Close OK No Exception ! - ret Status: true
-> Start Test openStream()
   Test openStream() OK!
-> Start Test testDoIOFailed() - Fails with an Assert !
-> Start Test testWriteFailed() - Catches IOException !
   Test testWriteFailed()  OK 
-> Start Test testOpenStreamFailed() - Fails with an IOException !
Mär 30, 2018 1:52:42 PM fileio.FileIO 
INFORMATION: FileIO constructor for fileName: c:/myFileTest
Mär 30, 2018 1:52:42 PM fileio.FileIO writeToStream
INFORMATION: Successfully written 19 Bytes to File: c:/myFileTest
Mär 30, 2018 1:52:42 PM fileio.FileIO 
INFORMATION: FileIO constructor for fileName: c:/myFileTest
Mär 30, 2018 1:52:42 PM fileio.FileIO writeToStream
SCHWERWIEGEND: java.io.IOException: FileOutputStream fos should NOT be null ! 
java.io.IOException: FileOutputStream fos should NOT be null ! 
	at fileio.FileIO.writeToStream(FileIO.java:122)
	at fileio.FileIOTest.testWriteToStreamFailed(FileIOTest.java:114)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	...

Mär 30, 2018 1:52:42 PM fileio.FileIO 
INFORMATION: FileIO constructor for fileName: c:/myFileTest
Mär 30, 2018 1:52:42 PM fileio.FileIO writeToStream
INFORMATION: Successfully written 55 Bytes to File: c:/myFileTest
Mär 30, 2018 1:52:42 PM fileio.FileIO doIO
INFORMATION: Byte written to File  c:/myFileTest: 55
Mär 30, 2018 1:52:42 PM fileio.FileIO 
INFORMATION: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: C:/myfile.txt
Mar 30, 2018 1:52:42 PM fileio.FileIO writeToStream
INFO: Successfully written 55 Bytes to File: C:/myfile.txt
Mar 30, 2018 1:52:42 PM fileio.FileIO doIO
INFO: Byte written to File  C:/myfile.txt: 55
Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO writeToStream
SEVERE: java.io.IOException: Message should NOT be null ! 
java.io.IOException: Message should NOT be null ! 
	at fileio.FileIO.writeToStream(FileIO.java:119)
	at fileio.FileIO.doIO(FileIO.java:51)
	at fileio.FileIOTest.testDoIOFailed(FileIOTest.java:173)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	...

Mar 30, 2018 1:52:42 PM fileio.FileIO doIO
SEVERE: java.io.IOException: Error writing to File: c:/myFileTest
  java.io.IOException: Message should NOT be null ! 
Mar 30, 2018 1:52:42 PM fileio.FileIO writeToStream
SEVERE: java.io.IOException: FileOutputStream fos should NOT be null ! 
java.io.IOException: FileOutputStream fos should NOT be null ! 
	at fileio.FileIO.writeToStream(FileIO.java:122)
	at fileio.FileIOTest.testDoIOFailed(FileIOTest.java:182)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	..

Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO writeToStream
SEVERE: java.io.IOException: Message should NOT be null ! 
java.io.IOException: Message should NOT be null ! 
	at fileio.FileIO.writeToStream(FileIO.java:119)
	at fileio.FileIOTest.testWriteFailed(FileIOTest.java:204)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	...

Mar 30, 2018 1:52:42 PM fileio.FileIO 
INFO: FileIO constructor for fileName: c:/myFileTest
Mar 30, 2018 1:52:42 PM fileio.FileIO openStream
SEVERE: java.io.FileNotFoundException: X:\myFileTestFailed (Das System kann den angegebenen Pfad nicht finden)
java.io.FileNotFoundException: X:\myFileTestFailed (Das System kann den angegebenen Pfad nicht finden)
	at java.io.FileOutputStream.open0(Native Method)
	at java.io.FileOutputStream.open(FileOutputStream.java:270)
	at java.io.FileOutputStream.(FileOutputStream.java:213)
	at java.io.FileOutputStream.(FileOutputStream.java:162)
	at fileio.FileIO.openStream(FileIO.java:87)
	at fileio.FileIOTest.testOpenStreamFailed(FileIOTest.java:77)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        ...

Using PHPUnit

×

Installation Directories and Software Versions

  • PHP was used from our XAMPP installation at: d:\xampp\php;
  • PHPUnit Installation Direcotory: D:\PHP\phpunit;
  • Windows 7
  • NetBeans 8.2
  • PHP 5.5.15
  • PHPUnit 4.8.36
  • phpunit-skelgen-1.2.1.

Install PHPUnit on Windows7

Verify your PHP version from your XAMPP install on Windows

D:\PHP\phpunit>  d:\xampp\php\php.exe --version
PHP 5.5.15 (cli) (built: Jul 23 2014 15:05:09)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans

Download PHPUnit and phpunit-skelgen package

Packages selected for download based on my PHP 5.5.15 version

  • phpunit-4.8.36.phar
  • phpunit-skelgen-1.2.1.phar
  • Directory location for PHPUnit files: D:\PHP\phpunit

Rename/Copy files

  • phpunit-4.8.36.phar -> phpunit.phar
  • phpunit-skelgen-1.2.1.phar -> phpunit-skelgen.phar

Create a new PHPUnit Command File

  • D:\PHP\phpunit> echo @php “%~dp0phpunit.phar” %* > phpunit.cmd

Modify your Windows Path varaible and verify your installation

Add to your windows PATH:   D:\PHP\phpunit;d:\xampp\php; ....

Finally verify the PHPUnit installation 
D:\Users\helmut> phpunit --version
PHPUnit 4.8.36 by Sebastian Bergmann and contributors.

Run a PHPUnit Sample from the Command Line

Directory structure matches Netbeans Project structure

 * Directory structure:
 * ├── PhpUnitTest
 *         ├── Calculator.php
 *         ├── tests
 *               ├── bootstrap.php
 *               ├── CalculatorTest.php

Calculator.php our PHP test class

class Calculator
{
    /**
     * @assert (0, 0) == 0
     * @assert (0, 1) == 1
     * @assert (1, 0) == 1
     * @assert (1, 1) == 2
     * @assert (1, 2) == 4
     */
    public function add($a, $b)
    {
        return $a + $b;
    }
}

bootstrap.php our PHP class to autoload PHP classes for testing

chdir("..");                // Note our tests directory is located just below our real PHP files  
echo "Current Working Directory:: " . getcwd() . "\n";
class Autoloader{
    public static function register(){
        //echo "Inside Register ..\n";
        spl_autoload_register(function ($class) {
            $file = str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
            if (file_exists($file)) {
                require $file;
                echo "+++ Autoload of PHP File:: " . $file . "\n";
                return true;
            }
            return false;
        });
    }
}
Autoloader::register();

Create the test class using phpunit-skelgen.phar

D:\xampp\htdocs\tc\PhpUnitTest> php \php\phpunit\phpunit-skelgen.phar --test Calculator
PHPUnit Skeleton Generator 1.2.1 by Sebastian Bergmann.
Wrote skeleton for "CalculatorTest" to "D:\xampp\htdocs\tc\PhpUnitTest\CalculatorTest.php".

Copy CalculatorTest.php  to the tests directory 
D:\xampp\htdocs\tc\PhpUnitTest> copy CalculatorTest.php tests
tests\CalculatorTest.php überschreiben? (Ja/Nein/Alle): ja

Test class generated by phpunit-skelgen.phar

/**
 * Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2018-03-04 at 12:37:11.
 */
class CalculatorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Calculator
     */
    protected $object;

    /**
     * Sets up the fixture, for example, opens a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp()
    {
        $this->object = new Calculator;
    }

    /**
     * Tears down the fixture, for example, closes a network connection.
     * This method is called after a test is executed.
     */
    protected function tearDown()
    {
    }

    /**
     * Generated from @assert (0, 0) == 0.
     *
     * @covers Calculator::add
     */
    public function testAdd()
    {
        $this->assertEquals(
          0,
          $this->object->add(0, 0)
        );
    }

    /**
     * Generated from @assert (0, 1) == 1.
     *
     * @covers Calculator::add
     */
    public function testAdd2()
    {
        $this->assertEquals(
          1,
          $this->object->add(0, 1)
        );
    }

    /**
     * Generated from @assert (1, 0) == 1.
     *
     * @covers Calculator::add
     */
    public function testAdd3()
    {
        $this->assertEquals(
          1,
          $this->object->add(1, 0)
        );
    }

    /**
     * Generated from @assert (1, 1) == 2.
     *
     * @covers Calculator::add
     */
    public function testAdd4()
    {
        $this->assertEquals(
          2,
          $this->object->add(1, 1)
        );
    }

    /**
     * Generated from @assert (1, 2) == 4.
     *
     * @covers Calculator::add
     */
    public function testAdd5()
    {
        $this->assertEquals(
          4,
          $this->object->add(1, 2)
        );
    }
}

Run the php Unit test

D:\xampp\htdocs\tc\PhpUnitTest\tests> phpunit --bootstrap bootstrap.php CalculatorTest.php 
Current Working Directory:: D:\xampp\htdocs\tc\PhpUnitTest
PHPUnit 4.8.36 by Sebastian Bergmann and contributors.

.
+++ Autoload of PHP File:: Calculator.php
...F

Time: 237 ms, Memory: 9.50MB

There was 1 failure:

1) CalculatorTest::testAdd5
Failed asserting that 3 matches expected 4.

D:\xampp\htdocs\tc\PhpUnitTest\tests\CalculatorTest.php:91

FAILURES!
Tests: 5, Assertions: 5, Failures: 1.

Return Codes from phpUnit

* PHPUnit Return Codes:   
 * .   Printed when the test succeeds.
 * F   Printed when an assertion fails while running the test method.
 * E   Printed when an error occurs while running the test method.
 * R   Printed when the test has been marked as risky (see Chapter 6).
 * S   Printed when the test has been skipped (see Chapter 7).
 * I   Printed when the test is marked as being incomplete or not yet implemented (see Chapter 7).      

PHPUnit and Netbeans Integration

Generic setup for PHPUnit within NetBeans IDE

Tools -> Option -> PHP -> PHP Framework -> PHPUnit
Image phpunit_img1.jpg  NOT Found

Create a new PHP project with the above Source for calcualater.php

Setup PHPUnit as test Provider

Right Click Project [ PhpUnitTest3] -> Properties -> Testing -> Add Folder
Image phpunit_img2.jpg  NOT Found

Create the PHP Test Classes

Right Click on Calculator.php -> Tools -> Create/Update Tests
Image phpunit_img3.jpg  NOT Found
 
Netbeans should create  CalculatorTest.php in out tests directory by running: 
"D:\xampp\php\php.exe" "D:\PHP\phpunit\phpunit-skelgen.phar" "--test" "--" "Calculator" 
    "D:\xampp\htdocs\PhpUnitTest3\Calculator.php" "CalculatorTest" "D:\xampp\htdocs\PhpUnitTest3\tests\CalculatorTest.php"
PHPUnit Skeleton Generator 1.2.1 by Sebastian Bergmann.

Wrote skeleton for "CalculatorTest" to "D:\xampp\htdocs\PhpUnitTest3\tests\CalculatorTest.php".  

Copy above bootstrap.php to our test folder and modify PHPUnit settings

Right Click Project [ PhpUnitTest3] -> Properties -> PHPUnit
Image phpunit_img4.jpg  NOT Found
Project Files and Directories sould now look like: 
* Directory structure:
 * ├── PhpUnitTest
 *         ├── Calculator.php
 *         ├── tests
 *               ├── bootstrap.php
 *               ├── CalculatorTest.php

Finally run PHPUnit tests

Right Click Calculator.php -> Test
Image phpunit_img5.jpg  NOT Found

Using Promises

×

About Promise

How Promises Work

A promise is an object which can be returned synchronously from an asynchronous function.
It will be in one of 3 possible states:

  • Fulfilled: onFulfilled() will be called (e.g., resolve() was called)
  • Rejected: onRejected() will be called (e.g., reject() was called)
  • Pending: not yet fulfilled or rejected

Promises following the spec must follow a specific set of rules:

  • A promise or “thenable” is an object that supplies a standard-compliant .then() method.
  • A pending promise may transition into a fulfilled or rejected state.
  • A fulfilled or rejected promise is settled, and must not transition into any other state.
  • Once a promise is settled, it must have a value (which may be undefined). That value must not change.
  • A Promise can be resolved or rejected, exclusively and only once.
  • A call to promise.then returns a promise, so that we can call the next promise
  • A value returned by a .then handler is immediately passed to the next handler
  • If the returned value is a promise, then the further execution is suspended until it settles.
  • A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server side
  • By default, fetch won’t send or receive any cookies from the server

Chained Promises

Chained Promises Sample

function testMe(myUrl, test, dataError) {
    dumpMessage("Testing Promise Chaining: "  + test, false);
    let url = myUrl;
    fetch(url)                           // fetch returns a promise       
        .then(response => {              // Our Success Handler returns response Object 
            if(response.ok) {
                if ( dataError) {        // Just for testing - Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok  at Chain Level 1');
        })  // Catches Exception for our Success Handler and for our Reject Handler 
    .catch(e => { 
        dumpMessage("Exception at Chain Level 1  : " + e,true );  
        throw new Error('Initial Fetch failed for URL: ' + url); 
    })
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    .then( (user) => {  dumpMessage("Username: " + user, false);  return fetch("https://api.github.com/users/"+user ); })
        // Load the response as json
    .then(response => response.json())     
        // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it)
    .then(githubUser => {
        dumpMessage("Displaying Github Avatar Image for 3 seconds!", false);  
        let img = document.createElement('img');
        img.src = githubUser.avatar_url;
        img.className = "promise-avatar-example";
        document.body.append(img);

        setTimeout(() => {
            img.remove();
            dumpMessage("Removed Github Avatar Image !", false);
        }, 3000); // (*)
    })
    .catch(err => {             // Catch all Hanlder return true/false for retry logic 
        dumpMessage("CatchAll Error-handler: " + err, true);
        });
} 

Promises and Error Handling

Overview

Pomisess Error Handling Diagram
Image promise_img1.jpg NOT Found

Handle Errors – Pattern 1


save().then(
  handleSuccess,
  handleError
);


  • Problem Exceptions in handleSuccess() are not catched

Handle Errors – Pattern 2


save()
  .then(handleSuccess)
  .catch(handleError)


Sample:
 fetch(url)                           // fetch returns a promise       
        .then(response => {              // Our Success Handler returns response Object 
            if(response.ok) {
                if ( dataError) {        // Just for testing - Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok  at Chain Level 1');
        })  // Catches Exception for our Success Handler and for our Reject Handler 
    .catch(e => { 
        dumpMessage("Exception at Chain Level 1  : " + e,true );  
        throw new Error('Initial Fetch failed for URL: ' + url); 
    })
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    ...

Handle Errors – Pattern 3


save()
  .then(
    handleSuccess,
    handleNetworkError    -> Handle and Fix Network Error
  )
  .catch(handleProgrammerError) -> Handle anf Fix ProgamError in handleSucessFunction


Sample:
    fetch(url)                      
        .then(response => {              // Our Success Handler 
            if(response.ok) {
                if ( dataError) {        // Mimicking a Data Proessing Error
                    throw new Error('Data Processing Error');
                }  
                return response.json();
            }
            throw new Error('Network response was not ok.');
        },      
        err => {    // Now we handle the error (rejection reason)
            dumpMessage("Handle error (rejection reason): " + err,true );  
               // Create a Dummy object
            dumpMessage("Create a Default Object to Finish Promise Chain");    
            myObj = { "name":"hhutzler", "id":999 };
            return myObj;
        } )
        .catch(e => { dumpMessage("Processing Error Chaining at Level 1  : " + e,true );  
            dumpMessage("Create a Default Object to Finish Promise Chain");    
            myObj = { "name":"hhutzler", "id":999 };
            return myObj;                   
        })
            //        ,        // Now we handle the error (rejection reason)
            //e => { dumpMessage("Initial Fetch Error: " + e,true );  throw new Error('Initial Fetch Error for URL' + url); } )
            //.then(response =>  response.json() )
    .then(jsonData => { dumpMessage("ID: " + jsonData.id + " - name : " + jsonData.name,false ); return(jsonData.name);} )
    ....

Reference

CORS Details

Setup

  • Html Page was loaded from: localhost
  • PHP file was loaded from: http://hhutzler.de

JavaScript Code

console.log("Testing Promise Chaining by Returning New Promises");
fetch("http://hhutzler.de/myID.php")
  .then(response =>  response.json() )
  .then(jsonData => { console.log("ID returned by node-fetch Module : " + jsonData.id + " - name : " + jsonData.name );} );

PHP Code


$myObj = new stdClass();
$myObj->id=999;
$myObj->name = "hhutzler";
$myObj->city = "Sattelmanmsburg";

$myJSON = json_encode($myObj);

echo $myJSON;

Expected CORS Error

 
Failed to load http://hhutzler.de/myID_NW.php: No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost' is therefore not allowed access. 
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

FIX – Add Access-Control-Allow-Origin to the PHP File

 
header('Access-Control-Allow-Origin: *'); 

$myObj = new stdClass();
$myObj->id=999;
$myObj->name = "hhutzler";
$myObj->city = "Sattelmanmsburg";

$myJSON = json_encode($myObj);

echo $myJSON;

Running my first Node.js project with Netbeans

×

Node.js Overview

Node.js processing model

  • Follows single threaded even loop processing model which can handle more and more concurrent requests easily
  • Another important point to keep in mind that Node.js architecture has two types of threads

Main Thread (Single Thread)

  • Called as internal thread as well, all the non I/O blocking requests are assigned using Main thread

Worker Thread(s)

  • Used for assigning blocking I/O heavy requests

Here are the details on how the single threaded event loop based processing works

  • Client sends the request to web sever
  • Node.js web server receives client requests from different clients, puts them into a queue. This queue is called event queue
  • Node.js has an internal component called event loop which runs the loop on event queue, assigns the request to a single thread if the request is non-blocking
  • In case of blocking (I/O heavy) request, event loop will assign this to worker thread
  • While worker thread is processing the request, event loop will continue to process other requests available in event queue

Node.js and Standard Webserver Flow-Diagramms

Node.js Flow Diagram
Image nodejs_img1.jpg NOT Found
Standard Webserver Flow Diagram
Image nodejs_img2.jpg NOT Found

My First Node.js Web Application

How it works ?

  • Netbeans runs our Code in 2 Steps
  • Step1: Netbeans start a nodejs server process to execute HTTP server Code
  • –> Our HTTP server is now listening on port : 9900
  • Step2: Netbeans starts a Chrome Browser Windows and connects to Port 9900
  • –> Our Browser display the Content provided by our Http Server

Run a first Node.js Project

Setup a Node.js project properties within Netbeans

Step 1 : Configure Netbeans Project Run Methode Step 2 : Configure Node.js Properties
Image nodejs_img1.jpg NOT Found Image nodejs_img2.jpg NOT Found

Add the following Code to main.js

const http = require('http');
const port = 9900;

const requestHandler = (request, response) => {
    let funcName = "requestHandler";
    if (request.method === 'GET' && request.url === '/' ){
        dumpMessage(funcName, "Request Method: GET - request URL: " + request.url);
        response.writeHead(200, {"Content-Type": "text/html"});
        response.end('Hello Node.js Server - at:  ' + getTime() + '!');
    }
};

const server = http.createServer(requestHandler);

server.listen(port, (err) => {
    let funcName = "server.listen";
    if (err) {
        return console.log(funcName, 'something bad happened: ' + err);
    }

  dumpMessage(funcName, 'server is listening on port: ' + port);
});

function dumpMessage(testName, mesg) {                                       
    console.log(getTime() + testName + ":: --> " + mesg);           
};
    
function getTime() {
    var ts = new Date();
    return ts.toLocaleTimeString()+ ":" + ts.getMilliseconds() + "  ";
};

Finally run the Node.js Code

Step 3 : Run the project
Image nodejs_img3.jpg NOT Found

Run above Node.js project manaully

Start the http Server

D:\xampp\htdocs\pvdata\testNodejs> "D:\Program Files\nodejs\node.exe"  "D:\xampp\htdocs\pvdata\testNodejs\main.js"
17:14:42:536  server.listen:: --> server is listening on port: 9900

Redirect your browser to the Listening Adress of our Web Server: http://localhost:9900

Browser Output
Image nodejs_img4.jpg NOT Found

Debugging a Node.js Application with Netbeans – Currently NOT working due to Netbeans Bug 271238

"D:\Program Files\nodejs\node.exe" "--debug-brk=9292" "D:\xampp\htdocs\pvdata\testNodejs\main.js"
 (node:10116) [DEP0062] DeprecationWarning: `node --debug` and `node --debug-brk` are invalid. 
Please use `node --inspect` or `node --inspect-brk` instead. 

Done.

Debugging ourNode.js Application manually

Start the node.js process with inspect-brk option

  • Enable inspector agent
  • Listen on default address and port (127.0.0.1:9229)
  • Break before user code starts
D:\xampp\htdocs\pvdata\testNodejs> node --inspect-brk main.js
Debugger listening on ws://127.0.0.1:9229/9391ed9f-a46d-4f49-bee2-b83d6dafb055
For help see https://nodejs.org/en/docs/inspector

Attach Chrome Devtools 55+ to the Node.js Debugger process

  • Open a Chrome DevTool 55+ Browser session with following URL: chrome://inspect
  • Select : Open dedicated DevTools for Node
Attach Chrome DevTools to Node.js Inspector
Image nodejs_img50.jpg NOT Found

CLI should respond with: Debugger attached

D:\xampp\htdocs\pvdata\testNodejs> node --inspect-brk main.js
Debugger listening on ws://127.0.0.1:9229/9391ed9f-a46d-4f49-bee2-b83d6dafb055
For help see https://nodejs.org/en/docs/inspector
Debugger attached.

Start Debugging

Debug Node.js by viewing Sources and setting Breakpoint
Image nodejs_img51.jpg NOT Found

Reference

Using JIRA

Overview JIRA

What is a Board ?

A board displays issues from one or more projects, giving you a flexible way of viewing, managing, and reporting on work in progress. There are two types of boards in Jira Software:

  • Scrum board: For teams that plan their work in sprints. Includes a backlog.
  • Kanban board: For teams that focus on managing and constraining their work-in-progress. Includes the option of a Kanban backlog.

What is a Epic ?

An epic captures a large body of work. It is essentially a large user story that can be broken down into a number of smaller stories. It may take several sprints to complete an epic.

What is a Sprint ?

A sprint — also known as an iteration — is a short (ideally two to four week) period in which the development team implements and delivers a discrete product increment, e.g. a working milestone version.

Introduction to JIRA & Agile Project Management

  • Introduction to JIRA & Agile Project Management
  • Install JIRA

    Downlaod JIRA

    Install JIRA on Windows 7

    D:\Program Files\Atlassian\JIRA\bin> dir \software
    20.01.2018  15:16       350.857.328  atlassian-jira-software-7.7.0-x64.exe
    
    As Administrator run: atlassian-jira-software-7.7.0-x64.exe
    Select: 
      Advanced Mode for installation 
      Ports 9080 9005     [ change Ports if needed ]
      Start as a service  [ Start JIRA as a service ]
    Login: 
      Email: 	   Helmut.Hutzler@gmail.com
      Benutzer:  Helmut.Hutzler
      Password:  .....it
    Config: 
      Running the Setup Wizard 
    

    Reference JIRA Installation

    Run JIRA on our local System JIRA

    • Note the Link below: http://localhost:9080/secure/Dashboard.jspa works ONLY on my local System !
    • Run JIRA Dashbaord

    Working with JIRA

    Create a Board using multiple Projects

    Create an Agile Board from Existing Projects
    Image jira_img1.jpg NOT Found
    Add multiple projects to the Board
    Image jira_img2.jpg NOT Found

    Add a new Project to an existing Board

    Boards -> View All Boards -> Configure -> Edit Filter Query -> Press Left Menu Button -> Add/Delete Projects
    Image jira_img3.jpg NOT Found

    Reconfigure JIRA by running Setup Wizard again

    Stop JIRA 
    D:\Program Files\Atlassian\JIRA\bin> stop-jira.bat
    
    Delete dbconfig.xml 
    D:\Program Files\Atlassian\Application Data\JIRA> del dbconfig.xml
    
    Delete Database Files 
    D:\Program Files\Atlassian\Application Data\JIRA\database> dir
    21.01.2018  10:16         2.838.528 h2db.mv.db
    21.01.2018  09:18            16.208 h2db.trace.db
                   2 Datei(en),      2.854.736 Bytes
                   2 Verzeichnis(se), 82.347.048.960 Bytes frei
    
    D:\Program Files\Atlassian\Application Data\JIRA\database> del *.db
    Möchten Sie "D:\Program Files\Atlassian\Application Data\JIRA\database\*" löschen (J/N)? j
    
    Open JIRA Baseurl to restart the configuration process: http://localhost:9080/
    

    Reference

    Using Mocha and Selenium

    Install Mocha, Chai, Selenium and Chromdriver

    Init local Repository

    D:\xampp\htdocs\pvdata\testSelenium\test> npm init
    ..
    package name: (test) testselenium
    version: (1.0.0)
    description: test Selenium and Mocha
    entry point: (googlePage.js) test_s5.js
    test command:
    git repository:
    keywords:
    author: Helmut
    license: (ISC)
    About to write to D:\xampp\htdocs\pvdata\testSelenium\test\package.json:
    
    {
      "name": "testselenium",
      "version": "1.0.0",
      "description": "test Selenium and Mocha",
      "main": "test_s5.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "Helmut",
      "license": "ISC"
    }
    
    Is this ok? (yes)
    ..
    

    Install local Nodejs Modules

    D:\xampp\htdocs\pvdata\testSelenium\test > npm install chromedriver@2.34.1 --save
    
    > chromedriver@2.34.1 install D:\xampp\htdocs\pvdata\testSelenium\test\node_modules\chromedriver
    > node install.js
    
    Downloading https://chromedriver.storage.googleapis.com/2.34/chromedriver_win32.zip
    Saving to D:\Users\helmut\AppData\Local\Temp\chromedriver\chromedriver_win32.zip
    Received 781K...
    Received 1568K...
    Received 2352K...
    Received 3136K...
    Received 3222K total.
    Extracting zip contents
    Copying to target path D:\xampp\htdocs\pvdata\testSelenium\test\node_modules\chromedriver\lib\chromedriver
    Done. ChromeDriver binary available at D:\xampp\htdocs\pvdata\testSelenium\test\node_modules\chromedriver\lib\chromedriver\chromedriver.exe
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN testselenium@1.0.0 No repository field.
    
    + chromedriver@2.34.1
    added 96 packages in 14.552s
    
    D:\xampp\htdocs\pvdata\testSelenium\test> npm install selenium-webdriver@3.6.0 --save
    npm WARN testselenium@1.0.0 No repository field.
    
    + selenium-webdriver@3.6.0
    added 14 packages in 4.256s
    
    D:\xampp\htdocs\pvdata\testSelenium\test> npm install chai --save
    npm WARN testselenium@1.0.0 No repository field.
    
    + chai@4.1.2
    added 7 packages in 2.129s
    

    Verify Local Module Status

    D:\xampp\htdocs\pvdata\testSelenium\test> npm  list --depth=0
    testselenium@1.0.0 D:\xampp\htdocs\pvdata\testSelenium\test
    +-- chai@4.1.2
    +-- chromedriver@2.34.1
    `-- selenium-webdriver@3.6.0
    

    Install Mocha as a Global Nodejs Module

    D:\xampp\htdocs\pvdata\testSelenium\test> npm -g install mocha@4.1
    D:\Users\helmut\AppData\Roaming\npm\mocha -> D:\Users\helmut\AppData\Roaming\npm\node_modules\mocha\bin\mocha
    D:\Users\helmut\AppData\Roaming\npm\_mocha -> D:\Users\helmut\AppData\Roaming\npm\node_modules\mocha\bin\_mocha
    + mocha@4.1.0
    updated 1 package in 4.61s
    

    Verify Global Package Status

    D:\xampp\htdocs\pvdata\testSelenium\test> npm  list --depth=0  -g
    D:\Users\helmut\AppData\Roaming\npm
    +-- eslint@4.16.0
    `-- mocha@4.1.0
    

    Testscripts

    Script: test_s4.js

    require('chromedriver');
    var assert = require('chai').assert;
    var selenium = require('selenium-webdriver');
    var test = require('selenium-webdriver/testing');
    var driver;
    var GooglePage = require('./googlePage.js');
    
    const timeOut = 15000;
     
    test.describe('Testing Google Search Page', function() {
      test.beforeEach(function() {
      	dumpMessage("-> beforeEach Hook");
        this.timeout(timeOut);
        driver = new selenium.Builder()
        	.forBrowser('chrome')
        	.build();
      	var googlePage = new GooglePage(driver);
        googlePage.view();
    
      });
      
      test.it( 'Working TestCase' , function(){  
        this.timeout(timeOut);
      	driver.getTitle().then(function(title){
      		dumpMessage("Found Page Title: " + title);
      		assert.equal("Google",title );
    	});
    	dumpMessage("--> Start Processing" ); 
    	driver.findElement(selenium.By.name('q')).sendKeys('webdriver');           // Fill in the  Search String for Google Search 
    	driver.findElement(selenium.By.name('btnK')).click();					   // Start the Google Search 	
        dumpMessage("--> Leaving Processing" ); 
      });
      
        test.it( 'Failing TestCase' , function(){  
        this.timeout(timeOut);
      	driver.getTitle().then(function(title){
      		dumpMessage("Found Page Title: " + title);
      		assert.equal("xGoogle",title );
    	});
    	dumpMessage("--> Start Processing" ); 
    	driver.findElement(selenium.By.name('q')).sendKeys('webdriver');           // Fill in the  Serach String to the 
    	driver.findElement(selenium.By.name('btnK')).click();					   	// Start the Google Search 	
        dumpMessage("--> Leaving Processing" ); 
      });
                
      test.afterEach(function() {
      	  var promise;
      	  var waitTime = 2000;       // Keep the browser window 2 seconds open before calling driver.quit()
      	  this.timeout(10000);       // Increase Timeout ot avoid Error: Timeout of 2000ms exceeded. 
    								 // If returning a Promise, ensure it resolves.        
        	promise = new Promise(function(resolve, reject){
            	setTimeout(function(){
               		dumpMessage("-> afterEach Hook Promise called - WaitTime: " +  waitTime );
               		driver.quit();
                	resolve();
            	}, waitTime);
     		});
        	// mocha will wait for the promise to be resolved before exiting
        	return promise;      		
      	dumpMessage("-> afterEach Hook");
      });
    
    });
    															 		// Helper Functions 							
    function dumpMessage(mesg) {                                        // console.log at top level will create a undefined message
    	console.log(getTime() + mesg);                                  // Provide a wrapper function which returns some data 
    	return "---------------------------------------------------";	
    }
    
    function getTime() {
    	var ts = new Date();
        return ts.toLocaleTimeString()+ ":" + ts.getMilliseconds() + "  ";
    }
    

    Script: googlePage.js

    var webdriver = require('selenium-webdriver');
     
    GooglePage = function GooglePage(driver) {
      this.driver = driver;
      this.url = "http://www.google.com/ncr";
    };
     
    GooglePage.prototype.view = function() {
      this.driver.get(this.url);
      return webdriver.promise.fulfilled(true);
    };
     
    module.exports = GooglePage;                     // export the module so in an be imported with the require syntax
    

    Running the Mocha/Selenium Testcase

    Testcase Overview

    • Testcase consists out of 2 tests !
    • Both tests starts and closes the Chromedriver
    • Testcase 1 should work !
    • Testcase 2 should fail !

    Testcase Details

    Command: mocha test_s5.js
    Image mocha_img4.jpg NOT Found

    A more complete Testcase

    NetBeans Output

    Press F6 in Netbeans to Start Test Scripts
    Image mocha_img10.jpg NOT Found

    Reference