Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Volte
Oct 4, 2004

woosh woosh
People who write and release bad software should be publicly made to feel bad. I don't see what's so hard to understand about this.

Adbot
ADBOT LOVES YOU

Volte
Oct 4, 2004

woosh woosh

shrughes posted:

Bullshit, it's not constructive. It's just somebody's little utility script. And they put it on github. You don't have to worry about that poo poo in utility scripts. The computer's not going to suddenly crash on you.
Bad code on the internet is harmful. Stop its proliferation by any means necessary.

Volte
Oct 4, 2004

woosh woosh

Cocoa Crispies posted:

I'm going to hand you a complete copy of all the code you've ever written and a revolver with a single bullet. We'll let you be responsible for any future bad code you proliferate.
I haven't put any of the code I've ever written on the internet.

Volte
Oct 4, 2004

woosh woosh
Everyone should be deeply ashamed of their code and make every attempt to conceal it. If you expose it to the open air, people will laugh and ridicule you, and deservedly so. Only a small amount of code that has ever been written is worth looking at, and the rest is highly corrosive and radioactive and should be kept in a lead container inside a concrete sarcophagus rather than on github where people might accidentally look at it.

Volte
Oct 4, 2004

woosh woosh

Jabor posted:

So this may or may not count as a horror:

http://openradar.appspot.com/13128709

checkDataDetectors routes any file:// uris (regardless of case) to DDResultCopyExtractedURL.
DDResultCopyExtractedURL asserts that the string it gets starts with the exact characters "file://".


At the very least, someone needs to write more comprehensive unit tests.
That's not a coding horror, it's just a bug.

Volte
Oct 4, 2004

woosh woosh
Maybe instead of trying to be ultra clever you should just write even n = n `rem` 2 == 0 like a normal person.

Volte
Oct 4, 2004

woosh woosh

Suspicious Dish posted:

I know how malloc and free work, I just didn't expect free to allow NULL as a special case, since to me it's an invalid input (you can't free a null pointer after all) and the C library typically just says "if you pass in an invalid input, it's your fault". But perhaps the utility of having unguarded frees is so convenient that they put it in there.
You can also realloc a null pointer and you can free a pointer by reallocing it to zero.

Volte
Oct 4, 2004

woosh woosh
The naive (and correct) solution is std::swap(a, b);

Volte
Oct 4, 2004

woosh woosh

seiken posted:

I stumbled across this thread on another forum that's from a year and a half ago so it's kind of old, but it's too good not to share.

Your opinion on the functionality of a template dynamic list

Basically, a clueless C++ guy ranting for several pages about how terrible the standard library is (while completely misunderstanding it) and showing off his crazy hand-rolled replacement where pretty much every function is called operator=. Some choice excerpts:


C++ code:
File = "Test.txt";
File = 'w';
File = "Write this to file.";
C++ code:
RendererProc Renderer;
ImageQueue Queue;
FileImage ImageLoader;

Queue = ImageLoader = "image1.jpg";
Queue = ImageLoader = "image2.jpg";
Queue = ImageLoader = "image3.jpg";

Renderer = Queue;
I'm crying

Volte
Oct 4, 2004

woosh woosh
When writing some code for doing MRI simulations I had a process for performing phase-encoding shots and counting them for the current image and cumulatively. Naturally, two accumulator variable names arose: cur_shot_count and cum_shot_count.

Volte
Oct 4, 2004

woosh woosh
https://github.com/search?q=extension%3Aphp+md5+password+mysql_query+%24_GET&type=Code&ref=searchresults

Volte
Oct 4, 2004

woosh woosh
I'm dying

Volte
Oct 4, 2004

woosh woosh
C++ code:
#include <cstdio>
 
template <bool cond, typename T, typename F>
struct tpl_if {
  typedef T val;
};
 
template <typename T, typename F>
struct tpl_if<false, T, F> {
  typedef F val;
};
 
 
template <int iter_outer>
class fizz {
  
  template <int iter, int temp, bool ones, char... c>
  struct fizz_impl {
    static char *go() {
      const int digit = temp % 10;
      const int next = temp / 10;
 
      typedef
      typename tpl_if<
        iter % 15 == 0,
    fizz_impl<iter-1, iter-1, true, 'f', 'i', 'z', 'z', 'b', 'u', 'z', 'z', ' ', c...>,
        typename tpl_if<
          iter % 5 == 0,
          fizz_impl<iter-1, iter-1, true, 'b', 'u', 'z', 'z', ' ', c...>,
          typename tpl_if<
            iter % 3 == 0,
            fizz_impl<iter-1, iter-1, true, 'f', 'i', 'z', 'z', ' ', c...>,
            typename tpl_if<
              ones,
              fizz_impl<iter, next, false, digit + '0', ' ', c...>,
              fizz_impl<iter, next, false, digit + '0', c...>
            >::val
          >::val
        >::val
      >::val branch;
      
      return branch::go();
    }
  };
 
  template<int iter, bool ones, char... c>
  struct fizz_impl<iter, 0, ones, c...> {
    static char *go() {
      return fizz_impl<iter-1, iter-1, true, c...>::go();
    }
  };
 
  template<bool ones, char... c>
  struct fizz_impl<0, 0, ones, c...> {
    static char *go() {
      static char r[] = { c... };
      return r;
    }
  };
        
public:
  static char *go() {
    return fizz_impl<iter_outer, iter_outer, true>::go();
  }
};
 
int main() {
  printf("%s", fizz<100>::go());
}

Volte
Oct 4, 2004

woosh woosh

That Turkey Story posted:

I wanted to go and do it anyway:

C++ code:
#include <iostream>
#include <type_traits>

template< char... C >
struct tmp_string
{
  typedef tmp_string type;
  static char const c_string[ sizeof...( C ) + 1 ];
};

template< char... C >
char const tmp_string< C... >::c_string[ sizeof...( C ) + 1 ]
  = { C..., '\0' };

template< class... Strings > struct combine;

template< char... LC, char... RC >
struct combine< tmp_string< LC... >, tmp_string< RC... > >
  : tmp_string< LC..., RC... > {};

template< class L, class R, class... Strings >
struct combine< L, R, Strings... >
  : combine< typename combine< L, R >::type, Strings... > {};

template< unsigned Digit > struct digit_to_string
  : tmp_string< static_cast< char >( '0' + Digit ) > {};

template< unsigned Value, class CurrString >
struct value_to_string_impl
  : value_to_string_impl
    < Value / 10
    , typename combine
      < typename digit_to_string< Value % 10 >::type
      , CurrString
      >::type
    > {};

template< class CurrString >
struct value_to_string_impl< 0, CurrString > : CurrString {};

template< unsigned Value >
struct value_to_string : value_to_string_impl< Value, tmp_string<> > {};

template<> struct value_to_string< 0 > : tmp_string< '0' > {};

template< unsigned Curr, unsigned End >
struct fizzbuzz_impl;

template< unsigned Curr, unsigned End >
struct fizzbuzz_impl_fizz_or_buzz
  : combine
    < typename std::conditional
      < Curr % 3 == 0, tmp_string< 'F', 'i', 'z', 'z' >, tmp_string<> >::type
    , typename std::conditional
      < Curr % 5 == 0, tmp_string< 'B', 'u', 'z', 'z' >, tmp_string<> >::type
    , tmp_string< ' ' >
    , typename fizzbuzz_impl< Curr + 1, End >::type
    > {};

template< unsigned Curr, unsigned End >
struct fizzbuzz_impl_not_fizz_or_buzz
  : combine
    < typename value_to_string< Curr >::type
    , tmp_string< ' ' >
    , typename fizzbuzz_impl< Curr + 1, End >::type
    > {};

template< unsigned Curr, unsigned End >
struct fizzbuzz_impl
  : std::conditional< Curr % 3 == 0 || Curr % 5 == 0
                    , fizzbuzz_impl_fizz_or_buzz< Curr, End >
                    , fizzbuzz_impl_not_fizz_or_buzz< Curr, End >
                    >::type {};

template< unsigned Curr >
struct fizzbuzz_impl< Curr, Curr > : tmp_string<> {};

typedef fizzbuzz_impl< 1, 101 >::type fizzbuzz;

int main()
{
  std::cout << fizzbuzz::c_string;
}
This one produces a single, c-style string at compile time.
So does mine, that's what the screenshot is. It's embedded in the data section.

Volte
Oct 4, 2004

woosh woosh
What have I done
C++ code:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
 
int main(int argc, char *argv[]) {
    double h;
    int i, k, m, r, d, e, f, g, u=31354, q=26982;
    double rtp=0.15915494309, y=0.41887902048, z=0.62831853071;
    for (i = 1; i <= 100; i++) {
        r=0.1*i; f=0; g=0;
        h=-(i-1)*y; m=round((0.5-rtp*atan2(-sin(h),-cos(h)))*15);
        k=0x30490610>>(m<<1);
        h-=y; m=round((0.5-rtp*atan2(-sin(h),-cos(h)))*15);
        d=r&0x1|(r>>1)&0x1|(r>>2)&0x1|(r>>3)&0x1;
        e=k&0x1|(k>>1)&0x1;
        h=-i*z; m=round((0.5-rtp*atan2(-sin(h),-cos(h)))*10);
        g=(k&0x1)*u; f=(k&0x1)*q; fputs((char*)&f,stdout); fputs((char*)&g,stdout);
        g=((k>>1)&0x1)*u; f=((k>>1)&0x1)*(q+3068); fputs((char*)&f,stdout); fputs((char*)&g,stdout);
        f=r+0x30; f|=(m+0x30)<<(d<<3); f&=~-e; fputs((char*)&f,stdout); putchar(' ');
    }
    putchar('\n');
    return 0;
}
http://ideone.com/r3VOfZ

Volte fucked around with this message at 07:46 on May 21, 2013

Volte
Oct 4, 2004

woosh woosh

Dren posted:

Volte did you make all that up just for this or is it a transformation you adapted?
I wrote it by hand for some reason. I wanted to see if I could do it in a single loop with no conditionals, divisions, or mod operators. Plus I made it as obfuscated as I could.

Volte
Oct 4, 2004

woosh woosh

Jabor posted:

A single loop with no conditionals or divmods is kind of easy though:

code:
int i = 1;
goto start;
while(i<100){
  printf("%d\n",i++);
  printf("Fizz\n",i++);
  printf("%d\n",i++);
  printf("%d\n",i++);
  printf("FizzBuzz\n",i++);  
start:
  printf("%d\n",i++);
  printf("%d\n",i++);
  printf("Fizz\n",i++);
  printf("%d\n",i++);
  printf("Buzz\n",i++);
  printf("Fizz\n",i++);
  printf("%d\n",i++);
  printf("%d\n",i++);
  printf("Fizz\n",i++);
  printf("Buzz\n",i++);
}
That's cheating

Volte
Oct 4, 2004

woosh woosh
Here is my solution with no conditionals or divmod. printf("1 2 fizz 4 buzz 6 ...")

Volte
Oct 4, 2004

woosh woosh
I'm all for a nice belittling tirade. It's the actual writing that makes me want to punch that guy in the dick.

Volte
Oct 4, 2004

woosh woosh

Ithaqua posted:

I find it pointless given that there are actual IDEs available because it's 2013.
Sometimes it's nice to just type code into a text editor.

Volte
Oct 4, 2004

woosh woosh
It's okay to use the mouse and arrow keys in Vim, it's not shameful despite what other people might try to tell you. You also get many other useful things like text objects, the f/t keys for jumping to a character on the line, 'cc' for replacing the current line, 'ciw' for erasing and changing the current word, 'ci(' for erasing the contents of the current set of parens, and other such things. Vim's good at navigation, but editing and transformation are its real strong suit. It takes a bit of time to learn but if you do a lot of coding you will become proficient before very long.

Volte
Oct 4, 2004

woosh woosh
I had a TA dock me because the comments in my printed-out code (the only way that we submitted assignments in that class) were not green

Volte
Oct 4, 2004

woosh woosh
Here is a picture of that assignment I found. It was so ridiculous I just had to take a photo (sorry it was from before I owned a phone with a decent camera). The assignment was to take an integer and print the word representation of it, which I did by recursively breaking the number up into three digit groups. The correct way was a massive case statement. When I confronted him about it he babbled something about not understanding my code and that he couldn't read the comments because they weren't green and so he couldn't tell where the comments stopped and the code began. I failed this assignment, other people got 100% even if their code didn't compile or make sense.

Volte
Oct 4, 2004

woosh woosh

pokeyman posted:

Did your code work? I'm having a hard time reading the blurry code. I do like these marker's comments though:
Yes, it worked fine, and was far more robust than the official solution which was basically "chop off various parts of the string and put them into a big switch statement".

Volte
Oct 4, 2004

woosh woosh

shrughes posted:

Volte, with that break/continue usage and exit(1) I have to conclude that you have no sense of control flow.
What, the exit(1) is only for when an impossible thing happens and it aborts the program.

Otto Skorzeny posted:

The part Volte left out is that after he showed his professor, the whole class had that assignment regraded and the prof got him an internship :unsmith:
This is almost literally true but it was during the regrading that the TA made the statement about green comments and I still got a poo poo mark. :smith:

Volte
Oct 4, 2004

woosh woosh

Rothon posted:

It would be more appropriate to assert(3) or abort(3) there.
assert() is a debug function which is totally inappropriate for an error message and abort() causes the program to die catastrophically with a core dump whereas exit() simply exits the program with an error code. In particular, destructors are not called on abort(). In my opinion the only time it is appropriate to use abort() is if there is no safe way to continue the program (e.g. a call to malloc failed).

Volte
Oct 4, 2004

woosh woosh
That's a spicy meatball

Volte
Oct 4, 2004

woosh woosh
It wasn't that guy, it was someone from #cobol

Volte
Oct 4, 2004

woosh woosh
Self-documenting code can only ever cover the what, it can never cover the why. That said, I prefer a well written external document explaining the rationale behind the code to having to sift through tons of comments strewn everywhere (particularly massive headers that are bigger than the function itself). Plus when the documentation is external and tied to a particular version of the code, there's less of a mindfuck when you realize that the comments are out of date and don't spend 20 minutes trying to reconcile what the documentation says versus what the code actually does.

Volte
Oct 4, 2004

woosh woosh
Everything that has ever worked will continue to work, forever.

Volte
Oct 4, 2004

woosh woosh
Don't be too hard on the guy, he's just trying to make some $² so he can afford three meals² per day.

Volte
Oct 4, 2004

woosh woosh

Biowarfare posted:

view source on that site, holy gently caress

code:
<div class="menu_links" onclick="$('#mensagem_caixa').dialog('open');
$('#conteudo_mensagem_caixa').html('<div>A publicação de hoje no Facebook 
está quase pronta!</div><div style=&quot;height:8px;&quot;>&nbsp;</div>
<div>Volte em alguns minutos que ela já estará publicada.</div>')">
<i class="fa fa-facebook link_menu_links" style="line-height:50px;"></i></div>
every single link
In it, voted 5

Volte
Oct 4, 2004

woosh woosh

sarehu posted:

null is seriously the most overrated "problem" ever. If you just pretend they don't exist, that a pointer in null state is just a trap representation that you can never read or compare to anything, i.e. that the nullness of a pointer is something you know statically, then you won't get any big costly problems in your software development (and certainly some non-nullable reference type being the default would be worse).
What happens when the libraries you call don't pretend that

Volte
Oct 4, 2004

woosh woosh

1337JiveTurkey posted:

Optional types feel half-assed when mixed with parametric polymorphism because while Foo[T] and Foo[Option[T]] may look closely related, they're nothing alike. Any situation where a method could return an Option[T], it should be equally valid to return a T rather than a Some[T]. So if T is covariant with Foo, then Foo[T] should be a subtype of Foo[Option[T]]. But if you want that in Scala for instance, then it needs to be Foo[Some[T]] which seems a tad ridiculous in terms of overhead when I want to use a Foo that always returns a T in a situation that calls for a Foo that may return a T without some sort of shim.
If Foo[T] is a subtype of Foo[Option[T]] then Option[T] is a subtype of Option[Option[T]] and the whole universe implodes

Volte
Oct 4, 2004

woosh woosh

Sinestro posted:

This is from my phone while I wait to get pizza, but isn't that just an implicit conversion via join :: (Monad m) => m (m a) -> m a? I mean, I guess you could make an Option[T] that didn't follow monad laws, but I'm pretty sure you'd have to a ctively try.
In that case I guess-- I thought he was talking about actual subtyping, not just implicit conversions. It's still a mess, and implicitly wrapping and unwrapping optionals is missing the point of Option. I mean, it's not just about having an extra value to represent 'nothing' added into the type, it's really an algebraic data type (1+α), and that structure separating None from Some(α) is an inherent part of it. Hiding it just because we want to be lazy is exactly the thing we're trying to avoid.

Volte
Oct 4, 2004

woosh woosh

SupSuper posted:

aaaaaaaaaaaaaaaaaaaaaaaaaaa
The funniest part is that the two supported databases are MongoDB and, uh, Dropbox.

Volte
Oct 4, 2004

woosh woosh
Even if you're not using test-driven development I still feel like writing the test that you would have written if you hadn't yet written the implementation (or even had any concept of what it might look like internally) is the best option for robustness. If you actually want to test internal implementation details (like making sure one private method calls another), then I would barely even consider that a unit test. If there is an internal implementation detail that needs to be asserted as part of a unit test, then it should be part of the interface contract (either literally, or at least documented as such). It might make sense to test that the internal quicksort partition function is called log(n) times, but that's because a quicksort function that does not satisfy that property is not quicksort, even if it sorts the array. It does not make sense to test that your mocked model received exactly 1 call to .Validate() and 10 calls to .OnPropertyChanged() over the course of one unit test. Instead, I would test that saving an invalid model results in a validation error, and that observers receive property change notifications.

I've heard arguments against using more than one "concrete" class per unit test, or that mixing multiple testable classes into one unit test is not actually unit testing, but more like integration testing, but I feel like that applies only if the dependencies are circular (i.e. don't test two services that depend on each other, within the same unit test -- break the loop using a mock/stub). If you have a Person model class along with a full set of PersonTests that verify it behaves correctly, then I have no problem using Person as a concrete class inside my ModelControllerTests instead of mocking a model class. As long as unit tests use only components that are themselves independently-unit tested (barring the component that is meant to be tested obviously), then it's still a unit test and only tests one thing as long as you don't stupidly write test code for your already-tested components.

Yes, if you break the Person model then it may break your ModelControllerTests, but I question the value of being in the situation where some essential code is broken but some other code that depends on it directly is not. The PersonTests should be failing as well, so you will never be masking the source of a test failure. I say there is greater value in the test environment mirroring the real environment as closely as possible, meaning as few ad-hoc testing implementations of things as possible. Obviously that depends on the project size and development strategy (and exactly what the impact of a failing test is), but I don't mind 25% of the tests turning red if one of the core services breaks. It seems like that is kind of a realistic result actually, and you gotta fix that poo poo pronto or else the same number of sweeping faulty interactions will (or could) happen in production.

The only time I can think of that I would substitute fake code for real code (i.e. mocks, stubs) is when the real code depends on external services or uncontrollable non-determinism. Even if you have to test a service that depends on random numbers, being able to supply a PRNG and seed to the service and then using a fixed value in the test is better than making a mock service that just returns a predefined list of numbers where randomness would otherwise go.

Another option would be to design services that have modular implementations-- your service for downloading from the web could have a DownloaderProvider that, by default, uses some HttpDownloaderProvider that fetches things from the web, but then you could supply a (complete and, most importantly, independently unit-testable) implementation of DownloaderProvider called e.g., LocalDownloaderProvider that fetches things from a local store or generates them. Then the LocalDownloaderProvider is not really a mock but a concrete implementation of an interface provided by your application, rather than just transient/axiomatic test code that exists within your unit testing environment. And then the thing I said earlier about composing already-tested components within a unit test applies: you can use the LocalDownloaderProvider to test the interface of your downloader service without having to create any "fake" test-only code or rely on a mocking library to do the right thing.

Volte
Oct 4, 2004

woosh woosh

Bognar posted:

Why would you willingly make debugging your tests harder? Digging through 25% of your tests to find which one actually broke seems ridiculous compared to knowing exactly which single test broke.
Because it makes the process of writing the application and writing the tests take less time and the time lost from glancing at your list of test suites to see which one is actually failing (and if you have a well-defined acyclic dependency graph between your tests, it would not be that hard to actually determine that programatically) may not be as significant as the time saved by not writing mocks all the time. I could ask "why would you willingly make writing your tests harder?". It's a trade-off like everything else.

edit: The other thing is that practically, a fundamental service breaking and causing everything else to break unexpectedly is probably not going to happen enough to make that possibility a fundamental pillar of your design. If you need to alter your fundamental service, then you need to run the tests for that service and make sure they don't break. If you do change a fundamental service and suddenly everything is broken, where's the mystery? Which part of that causes your tests to become hard to debug? Optimizing for the common case is popular in algorithm design, but it seems like optimizing for the worst case is the general modus operandi for software design. Why not assume the common case will hold (i.e., the uber-simple Person model does not magically break overnight) and make it robust enough that the worst case is not a complete catastrophe (i.e., you might have to spend 15 minutes looking through 100 test cases to find out which one is the truly broken one, although looking at the reasons for the secondary failures should also be enough to clue you in)

Volte fucked around with this message at 14:53 on Aug 3, 2015

Volte
Oct 4, 2004

woosh woosh

Jabor posted:

Except that takes forever since stepping through your "unit test" involves traversing thousands of lines of fairly unrelated components you decided it wasn't worth the effort to mock out.
Why would it take stepping through thousands of lines of code? Look at one of your failing tests, determine which assertion is giving an incorrect result. That incorrect result should correspond to another failing test -- find that failing test, and repeat until you find the source of the failure. Again, this is an exceptional case --- you might have to work through lunch.

And using implementations explicitly may not be the best option -- something like dependency injection could be used at the unit test level. If you have a test that only holds if there is some valid Model object available and allow that object to be injected, then you have a way to construct an acyclic dependency graph. If ModelControllerTests requires a Model and we inject Person, it would be trivial to detect Person-related failures by running PersonTests before even running the ModelControllerTests and bailing if any are found, thus avoiding spurious test failures. If you think about unit tests as proofs of propositions (i.e. the proposition "this class has been tested" NOT "this class is verified correct") then you can get very general tests of the form:

Tested(Foo), P1, P2 ⊢ Tested(Bar)

or even

∀F⊂Foo(Tested(F), P1, P2 ⊢ Tested(Bar(F)))

(in this case P1 and P2 might be something like "adding one should increase total by one" or whatever your normal unit tests would be, the proof of which is given by running the test and returning a passing result). A mock/stub Foo object is a valid proof of Tested(Foo) in this case, but if we already have perfectly reasonable real instances of Tested(Foo) lying around, then why introduce additional (in some cases deeply magical) instances of a potentially complex class into the test hierarchy?

Volte fucked around with this message at 15:48 on Aug 3, 2015

Adbot
ADBOT LOVES YOU

Volte
Oct 4, 2004

woosh woosh

Jabor posted:

So what happens if the "failing assertion" is due to something like, for example, a callback you expected to get never happened? Are you going to manually dig into wherever the callback was supposed to be called and then walk backwards until you find the part that didn't work? What if you don't actually know anything about that particular component?
That seems orthogonal to this concern. If your callback is supposed to be called, there should be a unit test that asserts that fact. If some secondary failure occurs because the callback was never called, you should be able to find the unit test that is failing because of it. And if the secondary failure is the only manifestation of that callback not being called, then your test coverage is incomplete, and using a mock would have covered up the faulty behaviour completely. And if you used unit tests with explicit dependency information, then you could ignore the whole "what is actually failing?" aspect, since the culprit would be trivial to deduce by highlighting the test cases whose dependencies were tested successfully but whose constituent tests failed.

I'm not saying mock objects aren't valuable in their place (whatever their place may be) but I reject the notion that treating components as an island for testing purposes is the only good way to do it. Dependency injection that knows how to associate a component with its test cases (or unit tests that are generic in the underlying implementation) seem like a fairly straightforward way to avoid strong coupling in the unit tests while still letting the components interact with each other more-or-less as they would in production. The more divergence between the testing environment and the production environment, the less effective the tests are going to be at rooting out production-time bugs. Hell, if you wanted to get fancy you could have it attempt a sequence of possible services to inject, choosing one whose tests succeed. If all you care about is "given a bunch of services that have been shown to be tested successfully..." then it doesn't actually matter what concrete implementation gets put in, as long as it behaves itself. And if it does matter, then that's an integration test.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply