Mocking Rust 🤪 and Testing 🧪

แชร์
ฝัง

ความคิดเห็น • 80

  • @peter9477
    @peter9477 2 ปีที่แล้ว +15

    Love the nonchalant making of a function representing a "long-running calculation that results in the answer 42", with no further explanation than that. ;-)

  • @calvinlucian387
    @calvinlucian387 2 ปีที่แล้ว +36

    As a newbie to Rust and a strong supporter of TDD this is probably the best byte-sized intro to testing on TH-cam today imo. Thanks a lot!

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      Thanks calvinlucian, glad you found it valuable!

    • @calebdeming5515
      @calebdeming5515 2 ปีที่แล้ว

      This guy is awesome

  • @0xccd
    @0xccd 2 ปีที่แล้ว +12

    Perfect timing. I've been struggling with mocks in Rust. Thanks!

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      Glad this video could help! Mocking in Rust is definitely a bit tricky.

  • @GeekMasher
    @GeekMasher 2 ปีที่แล้ว +22

    Great video. One thing I might have added to this is Rusts documentation testing. This is an amazing feature of the language. You will know that the documentation you have written compiles and runs properly as tests in `cargo test`.

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +7

      I actually haven't tried this, will check it out!

  • @mikerodent3164
    @mikerodent3164 8 หลายเดือนก่อน +2

    Thanks, this was useful. The "mockall" crate seems to be added to "dependencies". But surely that crate shouldn't be included when you compile. If you add it to "dev-dependencies", however, you can't then include "[automock]" in the non-test code. How does this aspect work?

  • @r31527
    @r31527 ปีที่แล้ว +4

    You're awesome! I've been procrastinating reading the Rust testing docs & you taught me everything I need to get started :)

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว

      Fantastic, really happy it was helpful!

  • @tosindaudu6595
    @tosindaudu6595 2 ปีที่แล้ว +7

    Please do refcell and cell next and soon. Thanks!

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      I've put it on the video idea list!

  • @Mustafa-099
    @Mustafa-099 ปีที่แล้ว

    This is Gold content on youtube!!! Loving every bit of it :)

  • @zsoltpasztori8770
    @zsoltpasztori8770 ปีที่แล้ว

    Ignore can also be used if the integration test needs some hardware to pass. For example, the library is used to calculate average temperature, and you want to test it with an actual sensor.

  • @oliverye5833
    @oliverye5833 ปีที่แล้ว +2

    If I use mockall, it requires mockall be a part of the dependency in the library. The example here wouldn't work if it's in the dev dependency. (Not sure if it's problem because I thought mock should be dev dependency as it's not related to actual context i.e api call)

    • @mikerodent3164
      @mikerodent3164 8 หลายเดือนก่อน

      Puzzled Rust mocking newb here: I've just asked the same question one year later. Did you get an answer to this? i.e. how to not include that mockall crate when compiling?

  • @necauqua
    @necauqua 2 ปีที่แล้ว +5

    There is one more interesting test binary flag, --nocapture.
    And in general, you didn't mention how the stdout is captured and printed only if the test fails - to keep successful test runs tidy

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      re: nocapture, I actually didn't know about this, thanks for pointing it out. and you're right, I should have mentioned that stdout is for successful tests.

  • @davidraymond8994
    @davidraymond8994 2 ปีที่แล้ว +4

    Awesome Rust videos. I am a newbie to Rust and am learning a lot from you thanks so much. Do you have a course offering or anything like that?

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +2

      Thanks David! I don't currently have a course offering (I'd love to have one though!) but I've seen many on Udemy and other online learning sites. Not sure which one is best, maybe ask in r/rust! Also Let's Get Rusty offers a course as well but I haven't tried it.

  • @andythedishwasher1117
    @andythedishwasher1117 2 ปีที่แล้ว +2

    Super useful breakdown! This will be an essential skill set as I dive into this idea I recently had. I'm trying to determine the potential advantages, if any, of using Rust to indirectly write Javascript via some kind of translation macro for the purposes of maintaining memory safety in the resulting Javascript. This would be different from what frameworks like Yew are doing (which is definitely awesome) by bundling Rust logic into beautiful web assembly that works reluctantly alongside JS like an incompetent older brother who just happened to inherit the keys to the workshop. We wouldn't get the same kind of performance advantages, but we would still benefit from the memory safety checks quite a bit. I discovered that effect when I was very quickly able to rebuild my calculator app from Yew to Vue 3 with minimal debugging and a lot less code.

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      Thanks Andy! That would be an interesting undertaking. I wonder if the brevity of the Vue version is more due to the Vue framework than JavaScript itself. Not sure. Personally when I build with Yew I don't find myself missing JS or React, but I do find myself missing Svelte 🙃

    • @andythedishwasher1117
      @andythedishwasher1117 2 ปีที่แล้ว

      @@codetothemoon Svelte is the big one I plan to explore next before I land on a target framework. Working on a rebuild of my HQ domain with Vue to assess the resulting code base, then probably working on a rebuild and/or additional experiment in Svelte depending on how the Vue build goes. Lots of work ahead!

  • @phenanrithe
    @phenanrithe 2 ปีที่แล้ว +1

    More and more often I find that hosting the unit tests in the same file as the source code is distracting and inconvenient without real benefit, so I'm mostly keeping them in a tests-rs file which is technically the same. Just prepend the file with "#![cfg(test)]". It's easier to switch files than lines in the same file in the test/code design loop, and the tests-rs file is like a detailed spec/example for the whole crate / submodule (or tests_ if it becomes too big). Unless there aren't many tests of course.

    • @phenanrithe
      @phenanrithe 2 ปีที่แล้ว

      (I have to write test-rs instead of test DOT rs otherwise silly TH-cam creates a link, which likely hides the comment as spam...)

    • @dealloc
      @dealloc ปีที่แล้ว

      I have never seen an example of changing a test file out being an actual use-case. Often because tests should be complete as a whole. Co-location makes it easier to find tests for the corresponding implementations imo.
      The only reason why I'd move unit tests to a separate file would be if the language didn't allow me to have it in the same place.
      If your tests become too big, that is also a good indication that your implementation likely does more than it needs. It makes it easier to discover where separation of concerns could be applied.
      But each to their own, as long as it's consistent I don't see any problem with either way of doing it.

    • @phenanrithe
      @phenanrithe ปีที่แล้ว +1

      @@dealloc I think it's a question of preference indeed, I just don't like to mix source code with test code. There's just no benefit, only inconvenience unless it's a very small file with very few tests.

  • @ClearerThanMud
    @ClearerThanMud 7 หลายเดือนก่อน

    Really like your Rust videos! Just wanted to say that I prefer to avoid mocking when possible by breaking a function into two parts: 1) a pure function that figures out what needs to be done and returns a description of that activity, and 2) an interpreter for that description. The interpreter will be straightforward and can often just be left for integration testing. All of the gnarly logic will be in the the pure function, which is now far easier to test: given these inputs, you should say that this needs to be done.

  • @flwi
    @flwi 2 ปีที่แล้ว +1

    Thanks for this helpful video! This will take some time to get used to. I work mostly in scala and just started learning rust. Mixing code and tests in a single file just feels wrong to me so far.

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      Cool - I personally think Scala is a fantastic language - sometimes I wish there was some way to get Rust's performance with a more Scala-like syntax. And yeah, putting tests and code in the same file feels a little weird to me still. But I think there are advantages, like not needing to maintain a test file / directory structure that is essentially a duplicate of the one that already exists for your code.

  • @jhiver1978
    @jhiver1978 ปีที่แล้ว +1

    Another flag worthwhile mentioning IMHO would be cargo test -- --nocapture to occasionally output debug info

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว

      good point, probably should have included that!

  • @adicide9070
    @adicide9070 11 หลายเดือนก่อน

    right so for somehting like this we'd definitely use a manual mock.

  • @mareknetzel
    @mareknetzel 2 ปีที่แล้ว +1

    Your videos are perfect!

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      Thanks Marek, very happy you find them valuable!

  • @noblenetdk
    @noblenetdk 2 ปีที่แล้ว +1

    How about the cargo plugin Nexttest? As alwas great explanation

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      Never heard of it, I've put it on the list of things to check out!

  • @mdshohidurrahman1121
    @mdshohidurrahman1121 2 ปีที่แล้ว +3

    Brain friendly,as always.

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      🧠 nice, brain friendliness is a big priority!

  • @irlshrek
    @irlshrek 2 ปีที่แล้ว

    I use --nocapture to print stuff in the tests!

  • @SArthur221
    @SArthur221 2 ปีที่แล้ว

    11:12 you said we were going to test the get_answer() function, but what seems to have happened is the exact opposite: that BigComputer's implementation gets called the way we prescribe...

    • @jordanwhittle8713
      @jordanwhittle8713 2 ปีที่แล้ว +1

      I could be wrong but I think the idea behind the ‘times’ part is to specify the way that get_answer USES BigComputer
      Imagine if BigComputer was actually a database and compute_answer created a record in the database. With times(1), you would be able to specify that get_answer only calls compute_answer once to create 1 record in the database instead of calling compute_answer 1000 times and creating 1000 records. You’re not testing the mocked object, just specifying how it must be used.

    • @SArthur221
      @SArthur221 2 ปีที่แล้ว

      @@jordanwhittle8713 good point

  •  11 หลายเดือนก่อน

    mockall should be in the devDependencies though right ?

  • @mufty9006
    @mufty9006 2 ปีที่แล้ว +2

    I always have to change my playback speed back to Normal. Can't afford to miss a word

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      Nice, I try to avoid any fluff!

    • @theroboman727
      @theroboman727 2 ปีที่แล้ว +1

      There is a browser extension I use called enhancer for youtube, where one of the features is the ability to change playback speed in 0.1x increments (can be configured) using ctrl+scroll wheel. Thought id mention this if you dont have anything similar already.

  • @masiarek
    @masiarek 2 ปีที่แล้ว +1

    Thank you!

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      You're welcome thanks for watching!

  • @spoq9637
    @spoq9637 ปีที่แล้ว +1

    Hi, I’m qa automation.
    can i use rust for api tests?
    Thx!

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว +1

      I don't think there's anything preventing you from doing this! Whether it's the best choice for such a task, I'm not sure.

  • @Marcos-tl2vy
    @Marcos-tl2vy 11 หลายเดือนก่อน

    Thanks!!!!🎉🎉❤

  • @kentheHar
    @kentheHar 2 ปีที่แล้ว +2

    Do the mocks end up in the normal binary or only the test binary?

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว +1

      Good question I'm actually not 100% sure about this, but my hope is that they would be pruned out because they are not used. I'd be surprised if they weren't.

  • @someon3
    @someon3 2 ปีที่แล้ว +1

    What are the real benefits of doing mock than construct the object directly?

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      Because constructing the object directly would make the test dependent on the behavior of that object - in unit tests we want to test a specific component in isolation, ideally without being affected by that component's dependencies and transitive dependencies. This is a bit easier to see when the component being tested has 10s or 100s of transitive dependencies.

    • @harleyspeedthrust4013
      @harleyspeedthrust4013 ปีที่แล้ว

      @@codetothemoon But if the thing being tested can't be isolated in real code because it depends on the thing we're mocking, then how do we expect to be able to test it in isolation? Doesn't that defeat the point of tests - to ensure that our code works when it runs in production?

    • @TehKarmalizer
      @TehKarmalizer ปีที่แล้ว

      @@harleyspeedthrust4013 that’s the argument. That when you test mocks, all you test is your ability to produce mocks. There are other types of testing, though. Unit testing is intended to test low level behavior.

  • @NexusGamingRadical
    @NexusGamingRadical ปีที่แล้ว

    So its impossible to mock structs without changes your code or the dependacy? :(

  • @samhanna7382
    @samhanna7382 2 ปีที่แล้ว +1

    is that the default vs code dark theme ?

    • @codetothemoon
      @codetothemoon  2 ปีที่แล้ว

      Yep I believe it's called Dark+!

  • @aazzrwadrf
    @aazzrwadrf ปีที่แล้ว +1

    what about logging?

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว

      specifically in a testing context or in general?

  • @mikechickenman
    @mikechickenman ปีที่แล้ว

    Don’t forget `cargo test - -nocapture`

  • @JosephDalrymple
    @JosephDalrymple ปีที่แล้ว

    It's all fun and games until you try and reference a trait in your struct so that you can mock it's _async methods_.
    EDIT emphasized. Should've been more specific in the beginning. 😅

    • @dealloc
      @dealloc ปีที่แล้ว

      This is the same as get_answer, but `impl` abstracts away what is really going on. The expanded version of using `impl` is this:
      ```
      fn get_answer(computer: &C, question: i32) -> String where C: BigComputer {
      // ...
      }
      ```
      With structs it would be the same format:
      ```
      struct GetAnswerMachine where C: BigComputer {
      computer: C;
      }
      ```
      You can also use the short-hand syntax:
      ```
      struct GetAnswerMachine {
      computer: C;
      }
      ```
      This is called dependency injection, because it allows you to switch out what "C" is, as long as it implements the "BigComputer" trait.
      If GetAnswer does not use the entire BigComputer, you can split up the BigComputer trait into smaller traits and compose them where needed. Smaller traits are easier to deal with and decreases the surface area, avoiding breaking changes that could affect all implementations that rely on large traits.

    • @JosephDalrymple
      @JosephDalrymple ปีที่แล้ว

      @@dealloc Unless your struct uses async methods 😅

  • @enokoner
    @enokoner 8 หลายเดือนก่อน

    good luck using mockall for nontrivial cases

  • @awnion
    @awnion 2 ปีที่แล้ว

    That's all great until you actually need something like async_trait + trait inheritance 🤣

  • @dr.maninderjitkaur7805
    @dr.maninderjitkaur7805 2 ปีที่แล้ว +2

    I am too sober for tNice tutorials, I'll be back later..

  • @jeffg4686
    @jeffg4686 ปีที่แล้ว

    Mocking Rust is the only way to get through learning it.
    I have full contempt by now, believe me.
    Question regarding asserts outside of tests - just in main code - would you always use debug_assert there. I'm still trying to figure out best practices in rust. My thought is that assert! in regular code is likely a bad idea (since it panics instead of allowing you a change to deal with an error result), but that debug_assert!() is useful since it's only in debug mode, but then that's kinda cheating your way out of having real error handling. So maybe even debug_assert isn't that useful in regular code. Maybe just as a placeholder to say "write some error handling here later"

    • @dealloc
      @dealloc ปีที่แล้ว

      Panicing is perfectly fine, mostly for internal implementations rather than user-facing APIs.
      It's better to panic and crash a program than ending up in a bad state that is unrecoverable and hard to debug. Assertions are not a replacement of error handling, but rather a compliment to it, by restricting the surface area of an implementation to avoid leaking internal details throughout the entire library/application, especially to user-facing APIs. Panics should be treated as bugs in the implementation. Rather than being a way to validate user input, it should be a way to validate input passed by the internal system.
      While you could pass Results everywhere in place of assertions but it makes it harder to avoid breaking changes in the code base and often easily leaks into user-land, where there is no way for the user to recover.
      Note that "user" in this case can be a user of a library, or a service interfacing with other services or programs.

  • @vanshkoul1200
    @vanshkoul1200 2 หลายเดือนก่อน +1

    look at me!!

  • @fleeb
    @fleeb 2 ปีที่แล้ว +1

    Ooooh... by 'Mocking Rust', you mean using mocks to imitate real objects. I thought this video was going to make fun of the language.

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว +1

      yeah, it was a somewhat lame play on words 🙃

  • @hiongun
    @hiongun ปีที่แล้ว +1

    Why everything becomes dead complicated when it comes to Rust?
    Boy, it's just testing. Do you really think this is 'simple' ?

    • @codetothemoon
      @codetothemoon  ปีที่แล้ว

      I don't think everything is complicated when it comes to Rust. But I think what you need to do to get mocking working is pretty awful. My gut feeling is that this complexity is due to the lack of support for inheritance, but I'm not 100% sure. In any case I hope the necessary language features get implemented that allow mocking to be a bit more concise.