Advertisement
Guest User

Untitled

a guest
Feb 7th, 2017
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 4.19 KB | None | 0 0
  1. /// Implemented by types that set up a lot of tests and run them all using
  2. /// some global config and a queue for JSON encoded results to be written to.
  3. trait TestCategory {
  4.     fn run(&self, Config, OutQueue<String>) -> Result<(), String>;
  5. }
  6.  
  7. /// Set up by a TestCategory and runs a specific test.
  8. /// Output is the type of the test results it will write a a queue
  9. trait Test<T> {
  10.     type Output: Clone + Serialize;
  11.    
  12.     fn execute(&self, &T, OutQueue<Self::Output>) -> Result<(), String>;
  13. }
  14.  
  15. /// There is one such trait like this for every TestCategory that exists, some
  16. /// for doing uploads, some for downloads. They all have the same "shape" in
  17. /// that they are basically traits capable of doing anything, but which simply
  18. /// have different names.
  19. trait HttpUploader {
  20.     type Data;
  21.     type Response;
  22.    
  23.     fn upload(&self, Self::Data) -> Result<Self::Response, String>;
  24. }
  25.  
  26. /// An implementation of TestCategory that might set up some tests around
  27. /// uploading some data to a server over HTTP.
  28. struct HttpTestCategory<'a> {
  29.    host: &'a str,
  30.     route: &'a str
  31. }
  32.  
  33. /// There can be multiple implementations of Test, which HttpTestCategory
  34. /// would setup and call execute() on.
  35. struct HttpTest1;
  36. struct HttpTest2;
  37.  
  38. /// An implementation of this category's uploader trait, which the
  39. /// HttpTestCategory will setup and provide to HttpTest1 and HttpTest2.
  40. /// A trait is used so that I can provide a mock implementation to test
  41. /// HttpTest1 and HttpTest2 with. This version would actually do HTTP requests.
  42. struct HttpUrlUploader;
  43.  
  44. /// I can have multiple implementations of HttpUploader
  45. impl HttpUploader for HttpUrlUploader {
  46.     type Data = (String, String); // A URL parameter name and value
  47.     type Response = Response; // Hyper response type
  48.    
  49.     fn upload(&self, data: Self::Data) -> Result<Self::Response, String> {
  50.         let client = hyper::client::Client::new();
  51.         let mut url = Url::parse("some.domain")?;
  52.         url.join(route);
  53.         url.set_query(Some(&format!("{}={}", data.0, data.1)));
  54.         client.get(url.as_str()).send().map_err(From::from)
  55.     }
  56. }
  57.  
  58. /// Here's where the magic happens.  My Test implementations restrict T
  59. /// on the HttpUploader trait so that they can only do the kinds of things
  60. /// the tests they implement are meant to do (i.e. not "download stuff")
  61. impl<T: HttpUploader<Data=(String, String)>> Test<T> for HttpTest1 {
  62.     type Output = TestData; // Doesn't matter
  63.    
  64.     fn execute(&self, uploader: &T, output: OutQueue<Self::Output>) -> Result<(), String> {
  65.         // Provide some data to uploader.upload()
  66.         // analyze the result
  67.         // output some observation data (TestData) to the output queue
  68.     }
  69. }
  70.  
  71. // TestCategory would be implemented to set up HttpTest1 and HttpTest2 along
  72. // with one separate HttpUploader for each. My code is designed so that every
  73. // such Test implementation would also supply an HttpUploader implementation.
  74. // For every TestCategory, there is a different version of the HttpUploader
  75. // trait- FtpDownloader for instance- which has the exact same shape (an input
  76. // type and an output type, with a single method that accepts some input data
  77. // and produces some output data). I feel like this pattern sucks for the
  78. // following reason:
  79. // 1. I have to define a new trait for every TestCategory I write (currently 4)
  80. // 2. Each of those traits looks exactly the same, but features some different
  81. //    names and input/output types, depending on the context
  82. // 3. I have to implement the appropriate trait from point (2) for every Test
  83. //    implementation
  84. //
  85. // My goal is to have a design that allows me to inject dependencies into
  86. // implementations of Test so that I can provide mock implementations of each
  87. // trait that actually does some IO stuff and do some more effective testing.
  88. // The problem is that I feel like I'm wasting a lot of code to implement these
  89. // traits, making my codebase quite messy, and over-abstracting. Each trait like
  90. // HttpUploader seems like a "Do Anything" trait to me because rather than
  91. // defining some specific, useful interface, each one is capable of doing
  92. // whatever you want provided an appropriate Data and Response associated type.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement