7

I'm developing an application that connects to a database, and I'm using DI/IOC. When running the entire application while developing, I naturally want to avoid hitting the DB. Currently, I have something like this in my composition root (using SimpleInjector):

#if DEBUG
    container.Register<IDbActions, MockDbActions>();
#else
    container.Register<IDbActions>(() => new RealDbActions(someParameter));
#endif

Is this good practice, or is there a better way to register mocks used for running the entire application during development?

Edit: This is not about unit testing. All classes can be unit tested regardless of what's in the composition root, because the IOC container is (naturally) never used during unit testing.

  • Are you suggesting to use DEBUG in the code under test, or in the unit test code itself? – John Wu Feb 20 '17 at 22:49
  • @JohnWu: Presumably, container.Register<IDbActions>(() => new RealDbActions(someParameter)); would never be executed in a Unit Test. – Robert Harvey Feb 21 '17 at 0:15
  • @JohnWu What Robert said is true, this is just for running the whole application while developing without hitting the DB. Unit tests would not use the composition root, or any IOC container for that matter. – cmeeren Feb 21 '17 at 6:53
  • 4
    What's wrong with hitting the DB? Just make sure the DB is a fake one that can be broken as often as you like. That way you can see exactly what your code is doing to the DB. Obviously you don't want to interact with the product database, but some sort of backup could be used to create a testing database. – Erdrik Ironrose Feb 21 '17 at 9:00
  • 2
    Can't you just connect to another DB? – Robbie Dee Feb 21 '17 at 10:40
7

I don't agree with this practice.

  1. If a unit tester is writing a test, and wants to mock the database a certain way, your code may interfere with that effort. He would have to mock your mock.

  2. How do you unit test the code that is not compiled in debug mode (the instantation of RealDbActions)? In your example, construction is trivial, but in some cases it may not be.

  3. You will be unable to run automated integration tests if the project is compiled in debug mode (the integration is mocked out). If you compile in release mode, it may be much harder to analyze the results.

  4. Your code base will be polluted with all of your mock types. Even if you exclude them from compilation, they are in your source tree and could affect diffs, merges, and other common activities whenever you change them.

  5. While a thin MockDbActions class may not be that bad, what if it has any dependencies (e.g. a mocking library)? Are you going to include references to those in your main project? Will it get deployed to your production data center? (As far as I know, Visual Studio does not support conditional compilation directives for references).

  • 1) and 2): Disagree. This doesn't interfere with unit testing. With unit testing, you instantiate classes directly and supply your own mocks. Here I'm registering mocks with the IOC container so that I can actually run the whole application without hitting the DB. 3) and 4) Fair points. 5) True, but in my case it's just an "empty" class. Anyhow, what do you suggest I do instead, so that I can run my application while developing and not hit the DB? – cmeeren Feb 21 '17 at 6:51
  • 2
    Oh I see, you're just monkeying with the composition root. If you were using design-time configuration then this wouldn't be an issue, you'd just have a separate config for a developer workstation and wouldn't have to change code. – John Wu Feb 21 '17 at 19:10
2

I don't disagree with this practice in general, but I would use it as a last resort. Anecdotally, I have used this when writing applications which connect to and read sensor hardware, but not had physical hardware available, and so without stubbing out the hardware communication the application would have been unable to run as a whole.

So, if you have no database to run against whatsoever, you can use this in a pinch to be able to run your app, but preferably hit a test database instead, and keep scripts to reset your test data to a known state, if applicable.

0

As @John Wu said, the solution has the downside, that it mixes productive and mocking code.

One option to avoid that could be to move the "DEV<>PROD switch" to the configuration. E.g. for entity framework (code first), it is easily possible to use a lightweight in-memory database instead of a heavy dbms. The configuration file can be configured depending on the build configuration (DEBUG/RELEASE).

If that is not possible and you have some mocking logic (more than one empty class), I would try to move the mocking logic in its own assembly to have a clear boundary.

However, in your case (with one empty class), I would just add a comment on top of the file and leave it in the same project... ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.