Viewing posts by davidkircos

End to End Testing for Expo Apps With CircleCI & Detox

It is becoming increasingly common for developers to begin to develop their apps with Expo with React Native. Expo is a great developer experience for getting started. However, some more advanced workflows for publishing production applications do not come out of the box. One of the missing workflows is end-to-end testing. So we set out to develop a method to run tests on our expo app using Continous Integration. Here is the configuration we came up with to successfully use CircleCI to test our Expo app using the testing framework Detox + Jest.

Below is our config.yml file. Our process works by performing the following steps:

  1. Install application dependencies.
  2. Log into the ExpoCLI using env vars (you have to set these in the CircleCI UI).
  3. Instruct Expo to do an iOS build with the target simulator.
  4. Download the build from Expo and extract it.
  5. Install testing packages.
  6. Run tests using detox test.


Here is a copy of our .detoxrc.json file

And here is our testing/config.json file

Our test runner takes about 10 minutes to set up and run a set of ~10 tests. The test setup is probably partially redundant and could be sped up. However, this works for us today! We hope this is helpful for you in setting up Expo End to End Testing!

Django + Postgres Views

Over the years, the Django ORM has become my go-to way to interact with a database from Python. It is an incredibly robust binding between the database and Python. As Postgres added advanced database features, the Django ORM kept up. An early example of this is Django's implementation of JSONB fields. The JSONB fields allowed you to save JSON as a field and interact with the JSON object's contents from the SQL query level. Django quickly supported this powerful feature. Awesome.

Another powerful feature added Postgres added is Views. Views are a simple database feature that allows you to create an alias for a common query. Suppose you were routinely running a SELECT statement on a table of transactions for the category outdoor_products:

SELECT * FROM tranactions_table WHERE product_type='outdoor'

In Postgres, you can save this query as a view for future reference.

CREATE OR REPLACE VIEW 'outdoor_product_transactions_view' AS 
(SELECT * FROM tranactions_table WHERE product_type='outdoor')
Then you can just
SELECT * FROM outdoor_product_transactions_view
And easily apply additional filters
SELECT * FROM outdoor_product_transactions_view WHERE amount > 1000

This feature is great for complex queries you are regularly repeating.

In the application I am currently building, there are complex queries that I run regularly on our API. I would also like our BI tools to benefit from these complex queries without repeating the logic in SQL. So I set out to create an easy way for a Django queryset to manage a Postgres view.

Here is the result:

from django.db import connection

def create_or_replace_view(view_name, view_qs):
    with connection.cursor() as cursor:
        queryset = view_qs
        compiler = queryset.query.get_compiler(using=queryset.db)
        sql, params = compiler.as_sql()
        sql = "CREATE OR REPLACE VIEW {view} AS {sql}".format(
            view=connection.ops.quote_name(view_name), sql=sql
        cursor.execute(sql, params)

from postgresviews.create_or_replace_view import create_or_replace_view
from ledger.querysets.formattedTransactionsQS import FormattedTransactionsQS

## Define Views Here
## NOTE: Renamed or Deleted Views will not automatically clean up the old version.
    "view_formatted_transactions", FormattedTransactionsQS().get_qs()
) is run on every deployment of our Django API. This allows me to use complex querysets with our API via Django Rest Framework and build a Postgres view of the queryset available to our BI tools. Magic! I hope you find this useful in your own applications!

How To Level A 4Runner Trunk For Less Than $40

I love to sleep in my car. It's the most convenient way to spend a night or two away from home, often far away from civilization, often biking or skiing.

With my Subaru Outback, I could simply put the seats down and sleep on a foam pad. Easy! However, I just switched to a Toyota 4Runner and there was a problem. The bed isn't flat! 

4Runners (gen5) have this nasty angled part in the middle, with a 1.5" difference between the trunk section and where the seats fold down. So I set out to make it flat.

My Design Criteria

  • Cheep. This can be done for less than $40.
  • Easy to make, easy to remove.
  • No modifications to the vehicle itself.
  • Can be done without access to a workshop.

Here is the result!

It takes up only 1.5" of vertical trunk space and doesn't need to be adjusted when you put your seats up.

How To Build It

What's needed:

  • 4x8' Plywood Sheet 3/4 inch thick. $20
  • Outdoor Carpet (or rubber mat if you don't have a staple gun) $15
  • Staple Gun (optional)

Step 1: Cut The Plywood

This design takes a single sheet of plywood (4'x8'x3/4") and cuts it into 2 sheets which are stacked on top of each other to make a 1.5" tall surface. Because the shape we want to fill has an angle on one side, the top piece is cut 2" inches longer to fill the angled gap. See the images below.

How it sits in the bed:

This only takes 3 cuts to complete, and your local Home Depot or Lowes will do it for you if you ask nicely!

Step 2: Secure The Top and Bottom Sheets Together

I simply screwed them together with a few 1 1/4" screws. 

Step 3: Carpet The Top Layer

I laid the structure upside down on top of an outdoor carpet. Pulled the carpet around all sides to the back, and stapled it in place. This way all the seams and staples are on the bottom.

(If you don't have a staple gun, and don't care about seeing the plywood when your trunk is open. simply put a rubber entryway mat over your sheets and cut it to size, Lowes sells 4'x6' foot mats which are large enough for $19.)

Step 4: Slide It In Place


I wanted to share this design because it is the simplest and least expensive one I could find, which doesn't really require any tools. Let me know if you build one!

Cherish Every Moment

At this moment, I feel stressed.

I feel pulled in 1000 directions.

I feel like I am doing too much; I feel like I'm not nearly doing enough.

I love the people I get to spend my days working with. 

I love what we are working on.

I love the person (and dogs) I get to go home to tonight.

I love the direction of our work and our lives. 

But, I feel lost in the moment.

A few days ago, I asked some friends the question, "what is missing from your life?"

I was delighted to hear their answer was, "nothing."

And I realized my own answer was "nothing."

I know at almost every other time in my life, I would have had a different answer.

Today I don't.

So why the feeling of loss?

Being grateful is a choice. I was choosing to experience the problems.

Not to just experience the moment.

Good or bad, every moment with every person I interact with, I can simply cherish.

Cherish every moment.

Skiing Into the Night

This past Friday night my friend Marco Vienna and I started running up Mt. Crested Butte on skis at midnight in a bid over Star Pass to end up in Aspen. This route is 37 miles, part of a Ski Mountaineering race called The Grand Traverse. We estimated the trip would take us 12 hours. Our friend (and support team) Danni Perri took this photo before the start of the race. Thank you so much for the support Danni!

For the first 5 hours of the race, we were doing great. We made it over Death Pass with plenty of time to spare and were actively passing other participants. Yes, it is actually called Death Pass - 2 people died on this section of the route while training for the race this year. There was a memorial on the racecourse.

We were heading up a high elevation valley called the Brush Creek Drainage. It was about 5 degrees out and I felt warm because we had been moving non-stop. Around this time we stopped for a few minutes to eat and drink water. And I got cold. It's not clear exactly what happened but my best guess is that I contracted mild hypothermia in my lungs which from then on made it extremely difficult to breathe.

We continued for the next two hours towards the next checkpoint, but my pace was much slower. Around six and a half hours in, I could only move a dozen meters before being forced to stop and catch my breath. This was a demoralizing rhythm, but we continued up the mountain. We made it to the Friends Hut checkpoint about 10 minutes before the cutoff. This checkpoint is right before a difficult climb over Star Pass, followed by the most difficult descent of the route.

While the rules of the race would have let us continue, at this point, I was pretty worried about how difficult it was for me to breathe. And there was no way I would have felt safe taking myself and friend up the pass. If my condition worsened at all, it would have put us both in an unacceptable amount of danger. If I couldn't get down under my own power, it would have been bad.

It was disappointing not to finish. But I am so happy we were able to get out there and try something truly difficult for our ability. Right as we decided not to continue, the sun finally rose after 7 hours of skiing in the dark, and we were rewarded with this view. It was the best silver lining.

Last fall I dragged Marco to Aspen for the Mountain Bike version of the Grand Traverse and this Spring he dragged me to Crested Butte for the ski version. Continuing the tradition we will be running this route in the fall (kidding... hopefully)