Orange Wheel: Connecting Vue and Flask Routes

For this part, I used the tutorial Full-stack single page application with Vue.js and Flask by Oleg Agapov. I chose this tutorial because:

  • Separation of frontend and backend into separate directories.
  • Sets up the project to use vue-router with Flask.
  • Sets up the config file so that when static assets are bundled, the output directory is at the root directory level. As a result, if I cd into my project, the first layer is frontend, backend, and dist. Both the frontend and backend were configured to connect to the dist directory.

Some issues I came across with the tutorial:

  • Inside the backend/app/app.py, the static_folder and template_folder path are different because I placed my app.py at a layer deeper. In the tutorial, the paths were ./dist/static and ./dist. In my case, because my app.py is inside the project directory app, I had to change it to ../../dist/static and ../../dist so it will find the output directory dist.
  • The tutorial didn’t mentioned how the Vue components were rendered in the frontend/App.vue. The answer? The magical tag of <router-view>. This tag is what vue-router us as a marker for where to render the component corresponding to the current route. Initially, I was confused since the site kept rendering the helloWorld code I put in the App.vue no matter what route I put in. Then I added the magic tag of <router-view></router-view> inside of <template> tag. Boom! The components contents now appear!

I also came across another issues that is specific to my project: testing.

Here is the error:

[Vue warn]: Unknown custom element: – did you register the component correctly? For recursive components, make sure to provide the “name” option.

Fear not. The Vue Test Utils documentation has a page just for Vue Router: Using with Vue Router. Here is my test code afterward:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Router from 'vue-router'
import App from '@/App'

const localVue = createLocalVue()
localVue.use(Router)

describe('App.vue', () => {
  it('renders Vue component App', () => {
    const wrapper = shallowMount(App, {
      localVue
    })
    expect(wrapper.isVueInstance()).toBe(true)
  })
})

Routing debugging took more than expected, so I will stop here and leave it as a separate post.


Orange Wheel: Backend with Flask. Trying out pytest.

Set up Directories & Environments:

To separate out an environment for my backend as well, the first thing I did was:

  • At the root directory of the project, used command mkdir backend to create a directory for backend.
  • Did cd backend to enter to the backend directory.
  • Did pip -m venv venv, which was what Python 3 use to create virtual environment. Python2 should be virtualenv venv. This project would be Python 3.
  • Invoked the virtual environment with . venv/bin/activate. It should work without problem. To deactivate the environment, just use the command deactivate.

Now for the framework.

I chosen Flask for this project, which I installed with pip install Flask. The reason was that backend shouldn’t be too complicated for this project, so a micro-framework known was ideal. There would be fetch call for the external membership website, a scraper to process the data, and maybe some storage to save user preferences.

Trying out Testing in Python:

Since I set up frontend testing, of course I would do backend. I hadn’t done testing in Python framework before though. Since pytest was what the Flask team had in their documentation on test, let’s go with that!

To get test coverage, I also installed pytest-cov. Together, the command was pip install pytest pytest-cov. From now on, if I want to run a test while also view code coverage, I just have to run pytest –cov=src.

I created a very basic scraper/app.py, with an index() that returned a string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import Flask


def create_app():
  app = Flask(__name__)

  @app.route('/')
  def index():
    return 'flask index page'

  return app


if __name__=='__main__':
  app = create_app()
  app.run(port=5000)

I then created a test directory with a test_basic.py to test for the string, but that’s not the only file I have to create. I also had to create what was named a conftest.py in the test directory. The pytest automatically discovers conftest.py, which is a file used to share fixture function. I am bit confuse on what fixture function is, but it seems to be a pytest feature that setup and teardown resources in a modular manner. The returned fixture objects are automatically passed into test functions as input arguments? I could be wrong though. If anyone can explain it well, please comment below!

Anyway, the test/test_basic.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
import pytest
from scraper.app import create_app

@pytest.fixture
def client(app):
    #create a test client for app
    return app.test_client()

@pytest.fixture
def app():
    app = create_app()
    app.debug = True
    return app

This creates the client fixture, which is pass as an argument in the test/test_basic.py:

1
2
3
4
5
6
7
8
9
import pytest
from flask import url_for

class TestLanding:
  def test_landing_page(self, client):
    response = client.get('/')
    print(response.data)
    assert response.status_code == 200
    assert response.data.decode('UTF-8') == 'flask index page'

The data in the returned response is actually in byte, so the test failed because the we are asserting against a string. I had seen tutorials where the solution was to do a assert text in response.data, but I prefer a more precise and less hack-y solution. I ended up doing assert response.data.decode(‘UTF-8’) == text.

So now I have a very basic test setup for both frontend and backend. Next up, setting up basic look of the app with Vue!