Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 194 additions & 2 deletions examples/lb3-application/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,13 +337,205 @@ $ npm start
Load [http://localhost:3000/](http://localhost:3000/) on your browser. This will
load the Express app, with mounted LB3 and LB4 applications.

### Need help?
## Running LB3 tests from LB4

You can run tests in an LoopBack 3 application from the LoopBack 4 application
it mounted on with command `npm test`.

We want the LoopBack 3 tests to use the LoopBack 4 server rather than the
LoopBack 3 application. The following guide shows how to run
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to briefly explain why here? e.g circular dependency or something else. Feel free to ignore :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agnes512 Taking the CoffeeShopApplication as an example, when you run the app, it sets up a LB4 rest server and mounts the LB3 app as its component. Therefore all endpoints from the LB3 app are exposed on that server.

And that's why to test those LB3 endpoints, we want to guarantee ^ as well 🙂.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can run tests in an LoopBack 3 application from the LoopBack 4 application
it mounted on with command npm test.

The statement is confusing. It's not clear in which directory the test would be run.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want the LoopBack 3 tests to use the LoopBack 4 server rather than the
LoopBack 3 application.

Does it use the LoopBack 3 application otherwise?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear in which directory the test would be run.

@hacksparrow The tests are kept in the LB3 application.

  • lb3-app
    • tests // we want to run the tests here
  • src
    • lb4app
    • __tests__

Copy link
Contributor Author

@jannyHou jannyHou May 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hacksparrow I looked at the doc again, and I believe

You can run tests in an LoopBack 3 application from the LoopBack 4 application
it mounted on with command npm test.

says clearly which directory the test would be run: "tests in an LoopBack 3 application". Or did you mean anything else?


- acceptance-level tests making HTTP calls to invoke application logic. e.g.
`POST /users/login`
- integration-level tests that are using JS API to call application logic. e.g.
`MyModel.create()`

### Adding LB3 Test Path in Command

In order to run LoopBack 3's tests from their current folder, add LB3 tests'
path to `test` entry in package.json:

- `"test": "lb-mocha \"dist/**tests**/\*_/_.js\" \"lb3app/test/\*.js\""`

In this case, the test folder is
[`/lb3app/test`](https://github.com/strongloop/loopback-next/tree/spike/lb3test/examples/lb3-application/lb3app/test)
from the root of the LoopBack 4 project.

This will run LoopBack 4 tests first then LoopBack 3 tests.

_To emphasize the setup steps and separate them from the test case details, all
the comprehensive test code are extracted into function `runTests`._

### Running Acceptance Tests

First, move any LoopBack 3 test dependencies to `package.json`'s devDependencies
and run:

```sh
npm install
```

In your test file:

1. When launch the Express server
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone who's interested in running my LB3 app tests, I'd be confused why I need Express server.

Copy link
Contributor Author

@jannyHou jannyHou May 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hacksparrow see my comment in #5403 (comment)
I think you are confused because you are new to this example. Please note the doc is added in the README.md file of the example repo.
This example demos two scenarios: "mounts lb3 app to lb4" and "mounts lb4 including lb3 to express server"


- 1.1 Update to use the Express server when doing requests:

```ts
// can use lb4's testlab's supertest as the dependency is already installed
const {supertest} = require('@loopback/testlab');
const assert = require('assert');
const should = require('should');
const {ExpressServer} = require('../../dist/server');

let app;

function jsonForExpressApp(verb, url) {
// use the express server, it mounts LoopBack 3 apis to
// base path '/api'
return supertest(app.server)
[verb]('/api' + url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}
```

- 1.2 Boot and start the Express app in your before hook, and stop the app in
the after hook:

```ts
describe('LoopBack 3 style tests - Launch Express server', function () {
before(async function () {
app = new ExpressServer();
await app.boot();
await app.start();
});

after(async () => {
await app.stop();
});

// your tests here
runTests();
});
```

2. When launch the LoopBack 4 application

- 2.1 Update to use the LoopBack 4 server when doing requests:

```ts
// can use lb4's testlab's supertest as the dependency is already installed
const {supertest} = require('@loopback/testlab');
const assert = require('assert');
const should = require('should');
const {CoffeeShopApplication} = require('../../dist/application');

let app;

function jsonForLB4(verb, url) {
// use the lb4 app's rest server
return supertest(app.restServer.url)
[verb](url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}
```

- 2.2 Boot and start the LoopBack 4 app in your before hook, and stop the app in
the after hook:

```ts
describe('LoopBack 3 style tests - launch LB4 app', function () {
before(async function () {
app = new CoffeeShopApplication();
await app.boot();
await app.start();
});

after(async () => {
await app.stop();
});

// your tests here
runTests();
});
```

Example of this use can be seen in
[`test/acceptance.js`](https://github.com/strongloop/loopback-next/tree/master/examples/lb3-application/lb3app/test/acceptance.js)
which has the same tests as
[`src/__tests__/acceptance/lb3app.acceptance.ts`](https://github.com/strongloop/loopback-next/blob/spike/lb3test/examples/lb3-application/src/__tests__/acceptance/lb3app.acceptance.ts),
but in LB3 style. And
[`test/authentication.js`](https://github.com/strongloop/loopback-next/tree/master/examples/lb3-application/lb3app/test/authentication.js)

Now when you run `npm test` your LoopBack 3 tests should be run along with any
LoopBack 4 tests you have.

Optional: Another option is to migrate your tests to use LoopBack 4 style of
testing, similar to `src/__tests__/acceptance/lb3app.acceptance.ts`.
Documentation for LoopBack testing can be found in
https://loopback.io/doc/en/lb4/Testing-your-application.html.

## Running Integration Tests

For the integration tests, LoopBack 3 models were bound to the LoopBack 4
application in order to allow JavaScript API to call application logic such as
`Model.create()`. This can be seen in
[`packages/booter-lb3app/src/lb3app.booter.ts`](https://github.com/strongloop/loopback-next/blob/spike/lb3test/packages/booter-lb3app/src/lb3app.booter.ts#L76-L85).

In order to retrieve the model from the application's context, `get()` can be
used as follows:

```ts
describe('LoopBack 3 style integration tests', function () {
let app;
let CoffeeShop;

before(async function () {
// If launch the LoopBack 4 application
// app = new CoffeeShopApplication();
app = new ExpressServer();
await app.boot();
await app.start();
});

before(() => {
// follow the syntax: lb3-models.{ModelName}
// If launch the LoopBack 4 application
// CoffeeShop = await app.get('lb3-models.CoffeeShop');
CoffeeShop = await app.lbApp.get('lb3-models.CoffeeShop');
});

after(async () => {
await app.stop();
});

// your tests here
runTests();
});
```

The syntax for LB3 model's binding key is `lb3-models.{model name}`.

Additionally, LB3 datasources are also bound to the LB4 application's context
and can be retrieved with a key in the syntax `lb3-datasources.{ds name}`.

Example integration tests can be found in
[`examples/lb3-application/lb3app/test/integration.js`](https://github.com/strongloop/loopback-next/tree/master/examples/lb3-application/lb3app/test/integration.js).

Example authentication tests can be found in
[`examples/lb3-application/lb3app/test/authentication.js`](https://github.com/strongloop/loopback-next/tree/master/examples/lb3-application/lb3app/test/authentication.js).

## Need help?

Check out our
[Slack](https://join.slack.com/t/loopbackio/shared_invite/zt-8lbow73r-SKAKz61Vdao~_rGf91pcsw)
and ask for help with this tutorial.

### Bugs/Feedback
## Bugs/Feedback

Open an issue in [loopback-next](https://github.com/strongloop/loopback-next)
and we'll take a look.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ module.exports = function (CoffeeShop) {
};
CoffeeShop.remoteMethod('greet', {
http: {path: '/greet', verb: 'get'},
returns: {type: 'string'},
returns: {arg: 'greeting', type: 'string'},
});
};
107 changes: 107 additions & 0 deletions examples/lb3-application/lb3app/test/acceptance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-lb3-application
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
const {supertest} = require('@loopback/testlab');
const assert = require('assert');
const {ExpressServer} = require('../../dist/server');
const {CoffeeShopApplication} = require('../../dist/application');
require('should');

let app;

function jsonForLB4(verb, url) {
// use the lb4 app's rest server
return supertest(app.restServer.url)
[verb](url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}

function jsonForExpressApp(verb, url) {
// use the express server, it mounts LoopBack 3 apis to
// base path '/api'
return supertest(app.server)
[verb]('/api' + url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}

function jsonForExternal(verb, url) {
// use the express server, its external apis doesn't have base path
return supertest(app.server)
[verb](url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}

describe('LoopBack 3 style acceptance tests - boot from Express server', function () {
before(async function () {
app = new ExpressServer();
await app.boot();
await app.start();
});

after(async () => {
await app.stop();
});

it('gets external route in application', function (done) {
jsonForExternal('get', '/ping').expect(200, function (err, res) {
assert.equal(res.text, 'pong');
done();
});
});

runTests(jsonForExpressApp);
});

describe('LoopBack 3 style acceptance tests - boot from LB4 app', function () {
before(async function () {
app = new CoffeeShopApplication();
await app.boot();
await app.start();
});

after(async () => {
await app.stop();
});

runTests(jsonForLB4);
});

function runTests(request) {
context('basic REST calls for LoopBack 3 application', () => {
it('creates and finds a CoffeeShop', function (done) {
request('post', '/CoffeeShops')
.send({
name: 'Coffee Shop',
city: 'Toronto',
})
.expect(200)
.end(function (err, res) {
assert(typeof res.body === 'object');
assert(res.body.name);
assert(res.body.city);
assert.equal(res.body.name, 'Coffee Shop');
assert.equal(res.body.city, 'Toronto');
done();
});
});

it("gets the CoffeeShop's status", function (done) {
request('get', '/CoffeeShops/status').expect(200, function (err, res) {
res.body.status.should.be.equalOneOf(
'We are open for business.',
'Sorry, we are closed. Open daily from 6am to 8pm.',
);
done();
});
});
});
}
Loading