Clean up the language so it reads better

This commit is contained in:
Erik Aker 2018-10-12 17:32:20 -07:00
parent 89336aee96
commit 18456baac5

View file

@ -153,7 +153,8 @@ Hmm. One passed and one failed! It looks like I *was* expecting a success
response in the second test, but I actually got a failure. We should fix that, response in the second test, but I actually got a failure. We should fix that,
but first I'd like to introduce `hspec-wai`, which will give us different but first I'd like to introduce `hspec-wai`, which will give us different
mechanisms for making requests of our application and validating the responses mechanisms for making requests of our application and validating the responses
we get. we get. We're also going to spin up a fake Elasticsearch server, so that our
server can think it's talking to a real database.
## *Mocking* 3rd Party Resources ## *Mocking* 3rd Party Resources
@ -201,11 +202,13 @@ build a simple app server that uses this client to retrieve documents. This
is somewhat contrived, but hopefully it illustrates the typical three-tier is somewhat contrived, but hopefully it illustrates the typical three-tier
application architecture. application architecture.
One note: we're also going to take advantage of `aeson-lens` here, which may One note: we're also going to take advantage of `lens-aeson` here, which may
look a bit foreign. The gist of it is that we're going to traverse a JSON look a bit foreign. The gist of it is that we're going to traverse a JSON
`Value` from Elasticsearch and try to extract some kind of document to `Value` from Elasticsearch and try to extract some kind of document to
return. return.
Imagine, then, that this is our real server implementation:
```haskell ```haskell
type DocApi = type DocApi =
"docs" :> Capture "docId" Integer :> Get '[JSON] Value "docs" :> Capture "docId" Integer :> Get '[JSON] Value
@ -232,7 +235,7 @@ getDocById esHost esPort docId = do
### Testing Our Backend ### Testing Our Backend
So the above represents our application and is close to a server we may So the above represents our application and is close to a server we may
actually deploy. How shall we test this application? actually deploy. How then shall we test this application?
Ideally, we'd like it to make requests of a *real* Elasticsearch server, but Ideally, we'd like it to make requests of a *real* Elasticsearch server, but
we certainly don't want our tests to trigger requests to a live, production we certainly don't want our tests to trigger requests to a live, production
@ -284,7 +287,7 @@ Hopefully, this will simplify our testing code:
```haskell ```haskell
thirdPartyResourcesSpec :: Spec thirdPartyResourcesSpec :: Spec
thirdPartyResourcesSpec = around_ withElasticsearch $ do thirdPartyResourcesSpec = around_ withElasticsearch $ do
-- we call `with` and pass our servant-server `Application` -- we call `with` and pass our own servant-server `Application`
with (pure $ serve (Proxy :: Proxy DocApi) $ docServer "localhost" "9999") $ do with (pure $ serve (Proxy :: Proxy DocApi) $ docServer "localhost" "9999") $ do
describe "GET /docs" $ do describe "GET /docs" $ do
it "should be able to get a document" $ it "should be able to get a document" $
@ -310,10 +313,10 @@ bodyMatcher _ body = case (decode body :: Maybe Value) of
-- success in this case means we return `Nothing` -- success in this case means we return `Nothing`
Just val | val == (Object $ HM.fromList [("a", String "b")]) -> Nothing Just val | val == (Object $ HM.fromList [("a", String "b")]) -> Nothing
_ -> Just "This is how we represent failure: this message will be printed" _ -> Just "This is how we represent failure: this message will be printed"
``` ```
What happens when we run these tests? Out of the box, `hspec-wai` provides a lot of useful tools for us to run tests
against our application. What happens when we run these tests?
``` ```
$ cabal new-test all $ cabal new-test all
@ -327,7 +330,8 @@ GET /docs
we can also do more with the Response using hspec-wai's matchers we can also do more with the Response using hspec-wai's matchers
``` ```
Fortunately, they all passed! Fortunately, they all passed! Let's move to another strategy: whole-API
testing.
## Servant Quickcheck ## Servant Quickcheck
@ -335,13 +339,13 @@ Fortunately, they all passed!
is a project that allows users to write tests for whole Servant APIs using is a project that allows users to write tests for whole Servant APIs using
quickcheck-style property-checking mechanisms. quickcheck-style property-checking mechanisms.
`servant-quickcheck` is great for asserting whole-API rules, such as "no `servant-quickcheck` is great for asserting API-wide rules, such as "no
endpoint throws a 500" or "all 301 status codes also come with a Location endpoint throws a 500" or "all 301 status codes also come with a Location
header". The project even comes with a number of predicates that reference header". The project even comes with a number of predicates that reference
the [RFCs they originate from](https://github.com/haskell-servant/servant-quickcheck/blob/master/src/Servant/QuickCheck/Internal/Predicates.hs). the [RFCs they originate from](https://github.com/haskell-servant/servant-quickcheck/blob/master/src/Servant/QuickCheck/Internal/Predicates.hs).
Thus, it's one way to assert that your APIs conform to specs and best In other words, it's one way to assert that your APIs conform to specs and
practices. best practices.
### Quickcheckable API ### Quickcheckable API
@ -389,7 +393,7 @@ servantQuickcheckSpec = describe "" $ do
-- `serverSatisfies` and the predicates also come from `servant-quickcheck` -- `serverSatisfies` and the predicates also come from `servant-quickcheck`
serverSatisfies api burl args (unauthorizedContainsWWWAuthenticate serverSatisfies api burl args (unauthorizedContainsWWWAuthenticate
<%> not500 <%> not500
<%> onlyJsonObjects <%> onlyJsonObjects -- this one isn't true!
<%> mempty) <%> mempty)
it "API doesn't have these things implemented yet" $ it "API doesn't have these things implemented yet" $
@ -428,7 +432,7 @@ Randomized with seed 1046277487
Finished in 0.4306 seconds Finished in 0.4306 seconds
``` ```
Hmm. It looks like we *thought* our API only return JSON objects, which is a Hmm. It looks like we *thought* our API only returned JSON objects, which is a
best practice, but in fact, we *did* have an endpoint that returned an empty best practice, but in fact, we *did* have an endpoint that returned an empty
body, which you can see in the printed response above: `Body: ""`. We should body, which you can see in the printed response above: `Body: ""`. We should
consider revising our API to only return top-level JSON Objects in the future! consider revising our API to only return top-level JSON Objects in the future!