Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// Implemented by types that set up a lot of tests and run them all using
- /// some global config and a queue for JSON encoded results to be written to.
- trait TestCategory {
- fn run(&self, Config, OutQueue<String>) -> Result<(), String>;
- }
- /// Set up by a TestCategory and runs a specific test.
- /// Output is the type of the test results it will write a a queue
- trait Test<T> {
- type Output: Clone + Serialize;
- fn execute(&self, &T, OutQueue<Self::Output>) -> Result<(), String>;
- }
- /// There is one such trait like this for every TestCategory that exists, some
- /// for doing uploads, some for downloads. They all have the same "shape" in
- /// that they are basically traits capable of doing anything, but which simply
- /// have different names.
- trait HttpUploader {
- type Data;
- type Response;
- fn upload(&self, Self::Data) -> Result<Self::Response, String>;
- }
- /// An implementation of TestCategory that might set up some tests around
- /// uploading some data to a server over HTTP.
- struct HttpTestCategory<'a> {
- host: &'a str,
- route: &'a str
- }
- /// There can be multiple implementations of Test, which HttpTestCategory
- /// would setup and call execute() on.
- struct HttpTest1;
- struct HttpTest2;
- /// An implementation of this category's uploader trait, which the
- /// HttpTestCategory will setup and provide to HttpTest1 and HttpTest2.
- /// A trait is used so that I can provide a mock implementation to test
- /// HttpTest1 and HttpTest2 with. This version would actually do HTTP requests.
- struct HttpUrlUploader;
- /// I can have multiple implementations of HttpUploader
- impl HttpUploader for HttpUrlUploader {
- type Data = (String, String); // A URL parameter name and value
- type Response = Response; // Hyper response type
- fn upload(&self, data: Self::Data) -> Result<Self::Response, String> {
- let client = hyper::client::Client::new();
- let mut url = Url::parse("some.domain")?;
- url.join(route);
- url.set_query(Some(&format!("{}={}", data.0, data.1)));
- client.get(url.as_str()).send().map_err(From::from)
- }
- }
- /// Here's where the magic happens. My Test implementations restrict T
- /// on the HttpUploader trait so that they can only do the kinds of things
- /// the tests they implement are meant to do (i.e. not "download stuff")
- impl<T: HttpUploader<Data=(String, String)>> Test<T> for HttpTest1 {
- type Output = TestData; // Doesn't matter
- fn execute(&self, uploader: &T, output: OutQueue<Self::Output>) -> Result<(), String> {
- // Provide some data to uploader.upload()
- // analyze the result
- // output some observation data (TestData) to the output queue
- }
- }
- // TestCategory would be implemented to set up HttpTest1 and HttpTest2 along
- // with one separate HttpUploader for each. My code is designed so that every
- // such Test implementation would also supply an HttpUploader implementation.
- // For every TestCategory, there is a different version of the HttpUploader
- // trait- FtpDownloader for instance- which has the exact same shape (an input
- // type and an output type, with a single method that accepts some input data
- // and produces some output data). I feel like this pattern sucks for the
- // following reason:
- // 1. I have to define a new trait for every TestCategory I write (currently 4)
- // 2. Each of those traits looks exactly the same, but features some different
- // names and input/output types, depending on the context
- // 3. I have to implement the appropriate trait from point (2) for every Test
- // implementation
- //
- // My goal is to have a design that allows me to inject dependencies into
- // implementations of Test so that I can provide mock implementations of each
- // trait that actually does some IO stuff and do some more effective testing.
- // The problem is that I feel like I'm wasting a lot of code to implement these
- // traits, making my codebase quite messy, and over-abstracting. Each trait like
- // HttpUploader seems like a "Do Anything" trait to me because rather than
- // defining some specific, useful interface, each one is capable of doing
- // whatever you want provided an appropriate Data and Response associated type.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement