Clean up the language so it reads better
This commit is contained in:
parent
89336aee96
commit
18456baac5
1 changed files with 16 additions and 12 deletions
|
@ -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!
|
||||||
|
|
Loading…
Reference in a new issue