Coder Social home page Coder Social logo

json_api_assert's Introduction

JsonApiAssert Build Status

Easily build composable queries for Elixir.

JsonApiAssert is built and maintained by DockYard, contact us for expert Elixir and Phoenix consulting.

Usage

JsonApiAssert is a collection of composable test helpers to ease the pain of testing JSON API payloads.

You can use the functions individually but they are optimally used in a composable fashion with the pipe operator:

import JsonApiAssert

payload
|> assert_data(user1)
|> assert_data(user2)
|> refute_data(user3)
|> assert_relationship(pet1, as: "pets", for: user1)
|> assert_relationship(pet2, as: "pets", for: user2)
|> assert_included(pet1)
|> assert_included(pet2)

The records passed must already be serialized. Read more about serializers below to see how to easily manage this with structs or Ecto models.

If you've tested JSON API payloads before the benefits of this pattern should be obvious. Hundreds of lines of codes can be reduced to just a handful. Brittle tests are now flexible and don't care about inseration / render order.

Record Serialization

The assert/refute functions expect json-api serialized maps. You can write these yourself but that can be verbose. Instead it is easier to manage your data as structs or better from Ecto models. Just use our built-in serializers.

import JsonApiAssert.Serializer, only: [serialize: 1]

user = %User{email: "[email protected]"}

user_json = serialize(user)

payload
|> assert_data(user_json)

As a convenience you can import the short-hand s function

import JsonApiAssert.Serializer, only: [s: 1]

payload
|> assert_data(s(user))

If you want to serialize JSON-API payloads from maps instead of structs, the only extra work you must do is provide a type argument

import JsonApiAssert.Serializer, only: [serialize: 2]

user_params = %{email: "[email protected]"}

user_json = serialize(user_params, type: "user")

payload
|> assert_data(user_json)

The built-in serializers should be good enough for most cases. However, they are not a requirement. Feel free to use any serializer you'd like. The final schema just needs to match the json-api resource schema:

%{
  "id" => 1,
  "type" => "author",
  "attributes" => %{
    "first-name" => "Brian",
    "last-name" => "Cardarella"
  }
}

Value matching via regex

Use a Regex as the value if that value is non-deterministic.

record = %{
  "id" => ~r/\d+/,
  "type" => "author",
  "attributes" => %{
    "first-name" => "Brian",
    "last-name" => "Cardarella"
  }
}

assert_data(payload, record)

A common use-case for this is when you are creating new records. The resulting value for the id is likely unknown and assigned at the run-time of the test suite. Using a Regex will allow you to assert that the id value is present and conforms to a value range and pattern that you expect.

Be careful however, you may get false-positives as the Regex matching can be done upon data as well as datum sets.

Authors

We are very thankful for the many contributors

Versioning

This library follows Semantic Versioning

Want to help?

Please do! We are always looking to improve this library. Please see our Contribution Guidelines on how to properly submit issues and pull requests.

Legal

DockYard, Inc. ยฉ 2016

@dockyard

Licensed under the MIT license

json_api_assert's People

Contributors

3h15 avatar bcardarella avatar cibernox avatar codyjroberts avatar daniel-xu avatar joshsmith avatar mike-north avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

json_api_assert's Issues

Unable to detect if attributes are missing from the actual JSON response

Version

Currently using the latest code from master

Issue

I was trying to use assert_data to assert that the server's JSON response included all the required attributes for a given resource.

In a test, I'm updating a resource by sending a PATCH request with some JSON API data. I then assert that the update is successful and that the server responds with data that matches the payload provided.

conn
|> patch(foo_resource_path(conn, :update, payload["id"]), payload)
|> json_response(200)
|> assert_data(payload)

Expected behavior

If the server fails to include certain attributes in the response (because they're not listed in the view), but the payload passed to assert_data does include them, I was expecting the function to detect this.

Actual behavior

The test passes when an attribute that's included in the expected payload does not in fact appear in the response. On the other hand, when the server response includes an attribute that is not in the payload argument, then the test fails as expected.

Problem when serializing datetimes

I'm completely new to Elixir so guess that I'm the one doing something wrong and need some guidance.
I'm using ja_serializer and ex_machina for my factories and doing nothing with the default timestamps for inserted_at and updated_at but I get:

left:  %{"updated-at" => "2017-03-25T12:16:38.063918"}
right: %{"updated-at" => ~N[2017-03-25 12:16:38.063918]}

How to fix this?

Serializer type defaults to underscore, not dash

This is a bit pedantic, but ja_serializer and ember-data default to dasherized types per json-api.org recommendation although the spec allows for both. I'm currently specifying the type with s/2 to get around this. Any reason for this? If not I'll throw a PR your way to add to my growing list. ๐Ÿ˜…

Mix.Utils.underscore/1 is deprecated, use Macro.underscore/1 instead

Thanks for make such a great mix. It's going well in my project, however, the warnings of deprecation are quite annoying.

warning: Mix.Utils.underscore/1 is deprecated, use Macro.underscore/1 instead
  (mix) lib/mix/utils.ex:244: Mix.Utils.underscore/1
  lib/json_api_assert/serializer.ex:61: JsonApiAssert.Serializer.put_type/3
  lib/json_api_assert/serializer.ex:34: JsonApiAssert.Serializer.serialize/2
  test/controllers/api/v1/survey_controller_test.exs:21: Tbn2020.Api.V1.SurveyControllerTest."test GET /api/v1/surveys returns surveys including questions and choices"/1
  (ex_unit) lib/ex_unit/runner.ex:302: ExUnit.Runner.exec_test/1

My Elixir and Mix are 1.4.2.

assert_data for create actions

When creating you likely do not know the returning id value. In this case we still want to user assert_data/2 however we need to have a different matching case.

We could disregard the id matcher if the returning has a single resource object.

Path to 1.0

  • links
  • meta
  • errors
  • Ecto.Date serialization (#2)
  • Ecto.Time serialization (#2)
  • Ecto.DateTime serialization (#2)

Voorhees Attribution

I think it would only be fair to attribute Voorhees for the original idea, especially when claiming that this is a rewrite of the Voorhees library, a library that was originally contributed to by the author until I left DockYard.

main window 2016-08-29 09-33-06

Conflicting type for serialized id

Both Ecto and json:api use id as a String. Currently JsonApiAssert.Serializer is returning id as an Integer. This is requiring me to do something like:

payload = put_in(payload["data"]["id"], model.id) 

If this isn't desired, I'd be happy to submit a PR. ๐Ÿป

New API and library rename

New name: jassert or jassert

One function that conn can be pipped through. The following API:

  • jassert(conn) - creates a new map %{payload: JSON.parse(conn.body), status: conn.status}

This function should be the first connection used. It will parse the conns body into a payload object.
Will assert that the payload is a properly formed json-api payload.
Will assert that the proper response headers are included.
Will assert that jsonapi object exists with correct meta data in the root of the document.
The payload and the response status are then stored in a new map object that can be consumed by corresponding jassert functions.

  • jassert(payload, :data, data)

This function will assert that data is contained in the data object of the payload.
If data is a list it will iterate over each member of the list and assert each individually.
If payload.status == 201 the id of data WON'T be expected. The data object on the payload MUST return a single resource object otherwise assertion fails.

  • jassert(payload, :included, included_data)

This function will assert that included_data is contained in the included object of the payload. If included_data is a list it will iterate over each member of the list and assert each individually.

  • jassert(payload, {:data, :included}, data, :relationship, type, relationship_data)

This function will assert that relationship_data is setup as a relationship for the given data. The path is followed either down data or included. The type can be singular or plural.
If relationship_data is a list each member of the list is asserted.

  • jassert(payload, :links, link_data)

This function will assert that link_data exists in the root of the document.

  • jassert(payload, {:data, :included}, data, :links, link_data)

This function will assert that link_data exists in the data resource object on the proper path.

  • jassert(payload, {:data, :included}, data, :relationship, type, relationship_data, :links, link_data)

This function will assert that link_data exists on the given relationship for the given resource object.

  • jassert(payload, :meta, meta_data)

This function will assert that meta_data exists in the root of the document

  • jassert(payload, {:data, :included}, data, :meta, meta_data)

This function will assert that meta_data exists in the given resource object

  • jassert(payload, {:data, :included}, data, :relationship, type, relationship_data, :meta, meta_data)

This function will assert that meta_data exists on the given relationship for the given resource object

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.