# Chainable Tests

**URL:** <https://internals.rust-lang.org/t/chainable-tests/20809>\
**Category:** tools and infrastructure\
**Created:** [May 12, 2024, 7:22pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809 "2024-05-12T19:22:49Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![DasLixou](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daslixou/32/12132_2.png) [@DasLixou](https://internals.rust-lang.org/u/DasLixou)\
**Post date:** [May 12, 2024, 7:22pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/1 "2024-05-12T19:22:50Z")

</div>

Hello! I have a compiler that I want to test with integration tests. Now currently it seems like there's no way of chaining tests.

So I propose a way for tests to make dependencies.

This would look like this:

```rs
#[test]
fn a() -> A {
    let a = A::new(); // this takes long
    assert!(a.is_valid());
    a
}

#[test(a)]
fn b(a: A) {
    let b = B::new(a); // this also takes long
    assert!(b.is_valid());
}

#[test(a)]
fn c(a: A) {
    let c = C::new(a); // this also takes long
    assert!(c.is_valid());
}

```

So here the tests should wait that all dependencies in the test attribute are done and were successfull, then their results get put in as the n-th first arguments for the function (so multiple dependencies are also possible)

---

<div class="post-metadata">

**Author:** ![idanarye](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/idanarye/32/3346_2.png) [@idanarye](https://internals.rust-lang.org/u/idanarye)\
**Post date:** [May 12, 2024, 8:48pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/2 "2024-05-12T20:48:13Z")

</div>

Nit: since both `b` and `c` depend on `a`, they should receive an `&A` rather than `A`.

---

<div class="post-metadata">

**Author:** ![idanarye](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/idanarye/32/3346_2.png) [@idanarye](https://internals.rust-lang.org/u/idanarye)\
**Post date:** [May 12, 2024, 8:58pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/3 "2024-05-12T20:58:16Z")

</div>

Also, this reminds me of the fixtures feature that other test frameworks have ([e.g. - pytest](https://docs.pytest.org/en/6.2.x/fixture.html#back-to-fixtures)). Tests don't depend on other tests, because the role of a test is to test something - not to prepare for another test. But they can depend on fixtures, which their job is to prepare for other tests.

Of course, while pytest's fixtures are name-based, in Rust they should be type-based:

```rust
impl Fixture for A {
    fn fixture() -> Self {
        Self::new()
    }
}

#[test]
fn a(a: &A) {
    assert!(a.is_valid());
}

#[test]
fn b(a: &A) {
    let b = B::new(a); // this also takes long
    assert!(b.is_valid());
}

#[test]
fn c(a: &A) {
    let c = C::new(a); // this also takes long
    assert!(c.is_valid());
}

```

Fixture dependencies is not as easy with a trait as it is with Python's full runtime introspection, but it's still doable:

```rust
impl Fixture for A:
    // lifetimes will probably be a pain, so I'll let smarter people figure them out
    type Dependencies = (&D1, &D2);
    
    fn fixture((d1, d2): Self::Dependencies) -> Self {
        Self::new(d1, d2)
    }
}

```

---

<div class="post-metadata">

**Author:** ![Enow\_Scott](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/enow_scott/32/11921_2.png) [@Enow\_Scott](https://internals.rust-lang.org/u/Enow_Scott)\
**Post date:** [May 13, 2024, 12:45pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/4 "2024-05-13T12:45:38Z")

</div>

Here's a small demo of how i would go about it

```rust
struct A {
}
impl A {
    fn new() -> Self {
       
        A {}
    }
    fn is_valid(&self) -> bool {
        true
    }
}
struct B {
}
impl B {
    fn new(a: &A) -> Self {
        B {}
    }
    fn is_valid(&self) -> bool {
        true
    }
}
struct C { 
}
impl C {
    fn new(a: &A) -> Self {
        C {}
    }
    fn is_valid(&self) -> bool {
        true
    }
}
fn setup_a() -> A {
    let a = A::new();
    assert!(a.is_valid());
    a
}
#[test]
fn test_b() {
    let a = setup_a();
    let b = B::new(&a);
    assert!(b.is_valid());
}
#[test]
fn test_c() {
    let a = setup_a();
    let c = C::new(&a);
    assert!(c.is_valid());
}

```

I defined setup functions that create and initialize the required objects for each test. Each test function then calls the corresponding setup function to obtain the necessary objects. If any setup function fails, the corresponding test will also fail.

---

<div class="post-metadata">

**Author:** ![DasLixou](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daslixou/32/12132_2.png) [@DasLixou](https://internals.rust-lang.org/u/DasLixou)\
**Post date:** [May 13, 2024, 1:09pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/5 "2024-05-13T13:09:20Z")

</div>

good catch

---

<div class="post-metadata">

**Author:** ![DasLixou](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daslixou/32/12132_2.png) [@DasLixou](https://internals.rust-lang.org/u/DasLixou)\
**Post date:** [May 13, 2024, 1:09pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/6 "2024-05-13T13:09:34Z")

</div>

but this would do the heavy computation twice

---

<div class="post-metadata">

**Author:** ![Enow\_Scott](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/enow_scott/32/11921_2.png) [@Enow\_Scott](https://internals.rust-lang.org/u/Enow_Scott)\
**Post date:** [May 13, 2024, 1:16pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/7 "2024-05-13T13:16:58Z")

</div>

Yeahhh thats right. I didn't say what you gave was wrong, Was just sharing my own idea

---

<div class="post-metadata">

**Author:** ![epage](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/epage/32/3171_2.png) [@epage](https://internals.rust-lang.org/u/epage)\
**Post date:** [May 13, 2024, 3:07pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/8 "2024-05-13T15:07:50Z")

</div>

**tl;dr the best way to move this idea forward is to help contribute to libtest-next.**

The [testing-devex team](https://github.com/rust-lang/testing-devex-team) was formed to coordinate effort on improving the testing experience within Rust.

Currently, our focus is on [json output for libtest](https://github.com/rust-lang/testing-devex-team/issues/1) so we can improve the UI and performance around `cargo test`. This is a bit slow going because we are trying to wrap up other commitments before we have enough bandwidth to focus on it.

The rest has not been discussed and agreed to within testing-devex.

[Last I talked to libs-api](https://epage.github.io/blog/2023/06/iterating-on-test/), they were concerned about the compatibility guarantees around libtest and instead wanted to see [first-class custom test harness support](https://github.com/rust-lang/testing-devex-team/issues/2). This would make it more natural to pull in a library to do fancy work like you are suggesting.

I've started [libtest-next](https://github.com/rust-lang/libtest-next) with the following goals

- Vet the json output design with more complex test interactions
- Vet the custom test harness work
- Be a focal point for custom test harness work so we can hopefully have one framework that libraries like trybuild, criterion/divan, etc plugin into rather than everyone re-inventing the wheel

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [May 13, 2024, 4:15pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/9 "2024-05-13T16:15:17Z")

</div>

> [@Enow\_Scott](#):
>
> It seems like you're proposing a way to specify dependencies between integration tests in Rust, where the output of one test function becomes input to another.

Is this ChatGPT? Do we have a policy against posting AI-generated messages?

> [@DasLixou](#):
>
> I have a compiler that I want to test with integration tests. Now currently it seems like there's no way of chaining tests.

This seems like a level of complexity which is more suited to custom test-driving code, rather than a standard macro.

Also, I don't know what your heavy tests are supposed to do, but you should seriously consider decoupling them. Introduce intermediate serializable representations, which would allow tests to be totally independent. You'll save yourself a lot of trouble down the line, both because independent tests can be run in parallel, saving your time (are you aware of [`cargo-nextest`](https://crates.io/crates/cargo-nextest)?), and because it makes the test less brittle and easier to use.

---

<div class="post-metadata">

**Author:** ![DasLixou](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daslixou/32/12132_2.png) [@DasLixou](https://internals.rust-lang.org/u/DasLixou)\
**Post date:** [May 13, 2024, 5:02pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/10 "2024-05-13T17:02:20Z")

</div>

Well my use case is that I want to test my compiler for different scenarios in AST and IR, until now that are all under a single test, but I would like to decouple them. So it wouldn't make them slower than they are right now

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [August 11, 2024, 5:02pm UTC](https://internals.rust-lang.org/t/chainable-tests/20809/11 "2024-08-11T17:02:32Z")

</div>

Closed on August 11, 2024, 5:02pm UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
