ztp/docs/03-refactored-tests-and-pre...

101 lines
3.3 KiB
Markdown
Raw Normal View History

Pre-commit checks and test refactorings. Re-reading the text, I made a number of changes. The first is that, while it is nice that Rust allows us to have unit tests in the file whose functionality we're testing, it's also nice to have the tests somewhere separate, and to have the tests be a little more modular. In the `./tests` folder, you can now see the same `health_check` test as the original, but in an isolated and cleaned-up form. Most importantly, the server startup code is now in its own function, with a correct return type that includes a handle to the spawned thread and the address on which that server is listening; tests can be run in parallel on many different ports and a lot of code duplication is eliminated. ``` rust type NullHandle = JoinHandle<()>; async fn spawn_server() -> (SocketAddr, NullHandle) { let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap(); let addr = listener.local_addr().unwrap(); let handle: NullHandle = tokio::spawn(async move { axum::Server::from_tcp(listener) .unwrap() .serve(app().into_make_service()) .await .unwrap(); }); (addr, handle) } ``` It is also possible now to add new tests in a straightforward manner. The Hyper API is not that much different from the Actix request API, and the Axum extractors seem to be straightforward. I suspect that what I'm looking at here with the handle is the idea that, when it goes out of scope, it calls a d In the introduction I said I was going to be neglecting CI/CD, since I'm a solo developer. That's true, but I do like my guardrails. I like not being able to commit garbage to the repository. So I'm going to add some checks, using [Pre-Commit](https://pre-commit.com/). Pre-Commit is a Python program, so we'll start by installing it. I'm using a local Python environment kickstarted with [Pyenv](https://github.com/pyenv/pyenv). ``` sh $ pip install pre-commit ``` And inside your project, in the project root, you hook it up with the following commands: ``` sh $ pre-commit install $ pre-commit sample-config > .pre-commit-config.yaml ``` I'm going with the default from the rust pre-commit collection, so my `.pre-commit-config.yaml` file looks like this: ``` yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v3.1.0 hooks: - id: check-byte-order-marker - id: check-case-conflict - id: check-merge-conflict - id: check-symlinks - id: check-yaml - id: end-of-file-fixer - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/pre-commit/pre-commit rev: v2.5.1 hooks: - id: validate_manifest - repo: https://github.com/doublify/pre-commit-rust rev: master hooks: - id: fmt - id: cargo-check - id: clippy ``` ... and with that, every time I try to commit my code, it will not let me until these tests pass. And I *like* that level of discipline. This is low-level validation; it won't catch if I put addition where I meant subtraction, or if I have a comparison going in the wrong direction, but at least the basics are handled and, more importantly, the formatting and styling is consistent throughout all of my code.
2023-03-22 00:52:44 +00:00
+++
title = "Refactored Tests and Pre-Commits"
date = 2023-03-20T17:38:12Z
weight = 3
+++
## Chapter 3.7 (Sort-of): A brief diversion
Re-reading the text, I made a number of changes. The first is that, while it is
nice that Rust allows us to have unit tests in the file whose functionality
we're testing, it's also nice to have the tests somewhere separate, and to have
the tests be a little more modular.
In the `./tests` folder, you can now see the same `health_check` test as the
original, but in an isolated and cleaned-up form. Most importantly, the server
startup code is now in its own function, with a correct return type that
includes a handle to the spawned thread and the address on which that server is
listening; tests can be run in parallel on many different ports and a lot of
code duplication is eliminated.
``` rust
type NullHandle = JoinHandle<()>;
async fn spawn_server() -> (SocketAddr, NullHandle) {
let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let handle: NullHandle = tokio::spawn(async move {
axum::Server::from_tcp(listener)
.unwrap()
.serve(app().into_make_service())
.await
.unwrap();
});
(addr, handle)
}
```
It is also possible now to add new tests in a straightforward manner. The
Hyper API is not that much different from the Actix request API, and the Axum
extractors seem to be straightforward. I suspect that what I'm looking at here
with the handle is the idea that, when it goes out of scope, it calls a d
## Adding some checks
In the introduction I said I was going to be neglecting CI/CD, since I'm a solo
developer. That's true, but I do like my guardrails. I like not being able to
commit garbage to the repository. So I'm going to add some checks, using
[Pre-Commit](https://pre-commit.com/).
Pre-Commit is a Python program, so we'll start by installing it. I'm using a
local Python environment kickstarted with
[Pyenv](https://github.com/pyenv/pyenv).
``` sh
$ pip install pre-commit
```
And inside your project, in the project root, you hook it up with the following commands:
``` sh
$ pre-commit install
$ pre-commit sample-config > .pre-commit-config.yaml
```
I'm going with the default from the rust pre-commit collection, so my
`.pre-commit-config.yaml` file looks like this:
``` yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit
rev: v2.5.1
hooks:
- id: validate_manifest
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: cargo-check
- id: clippy
```
... and with that, every time I try to commit my code, it will not let me until
these tests pass. And I *like* that level of discipline. This is low-level
validation; it won't catch if I put addition where I meant subtraction, or if I
have a comparison going in the wrong direction, but at least the basics are
handled and, more importantly, the formatting and styling is consistent
throughout all of my code.