Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable unit testing of data portal operation methods #1736

Closed
rockfordlhotka opened this issue Jul 27, 2020 · 8 comments
Closed

Enable unit testing of data portal operation methods #1736

rockfordlhotka opened this issue Jul 27, 2020 · 8 comments

Comments

@rockfordlhotka
Copy link
Member

From @JasonBock in gitter

It's true that in systems where DI is used, you can write unit tests with the need for a container. For example, in ASP.NET Core, if you create a controller that takes a dependency on construction, it's trivial to write a unit test for the controller that gets an instance of the dependency. No DI container needed. But with CSLA, since the BOs have a hard dependency on CSLA's infrastructure to manage its' lifetime, we have to get a container in play. That's fine by me if I can run tests in parallel and CSLA isn't using a shared container somewhere within its' code to resolve dependencies marked with [Inject].

What I'd really like to see is not something using Reflection for testing as @rockfordlhotka alluded to. Rather, if I could create an instance of a DataPortal<> that takes a reference to a specific IServiceCollection and that DP is used in my tests, then we're golden. I don't know if that's possible right now - again, I don't quite know where CSLA is storing the collection reference. Seems like AddCsla() is using ApplicationContext to do it, which is a little disheartening. I get that in ASP.NET Core, you're going to have one container, so for AddCsla(), that's fine, but I really hope it's passing that container to DataPortal, rather than DataPortal going to a shared location for the container.

Response:

I understand what you want, and I'm talking through the practicalities of making it happen.

  • You want to use the client-side data portal like normal, but create an instance by passing in a custom service provider - ✔
  • You want the normal data portal flow from client to (logical) server, such that the server uses the same service provider you passed to the client - ✔
  • You want the Inject parameters to come from that service provider - ✔
  • You really DON'T want to change autocloneonupdate, because that changes the behavior of the data portal, so your test results may not match your runtime results - ❌
  • You also don't want me to radically alter the behavior of the data portal for testing, because that would likely introduce inconsistencies between tests and runtime - ❌
  • You could just use the server-side data portal directly - bypassing the client-side data portal entirely - which would be easy to implement, but would also not necessarily provide the same behaviors in tests as runtime - ❓
@JasonBock
Copy link
Contributor

If I have to test through the server-side DataPortal, which, as I suggested, could get an instance of a IServiceCollection (or provider, whatever - the point is it's referring to a specific instance and not reaching up to ApplicationContext) on construction, and (maybe?) I use AutoCloneOnUpdate (not sure why the red X is next to that) to prevent serialization/proxy needs, that should be sufficient. Unit tests IMO don't have to match 100% the runtime conditions that should happen with integration and/or end-to-end tests; they're just verifying that the code under tests works as expected.

@rockfordlhotka
Copy link
Member Author

Unit tests IMO don't have to match 100% the runtime conditions that should happen with integration and/or end-to-end tests; they're just verifying that the code under tests works as expected.

In that case the easiest solution is probably to have people directly create and use a server-side data portal instance - just skip all the complexity of everything prior.

Or even simpler, have a Csla.Testing namespace that includes a way to invoke your data portal operation methods directly, without even going through all the server-side pipeline.

@JasonBock
Copy link
Contributor

In that case the easiest solution is probably to have people directly create and use a server-side data portal instance - just skip all the complexity of everything prior.

Completely agree. I think the right thing is to still go through the DataPortal and not a "special test" thingee that would just call the DP operation, because IIRC other "things" happen in the operation pipeline that I'd also want to confirm in a test (maybe, not 100% sure on this).

@rockfordlhotka
Copy link
Member Author

other "things" happen in the operation pipeline that I'd also want to confirm in a test

Yeah, but some other things happen in the client-side data portal too (like RunLocal) that you might want to test - which is why I've been filtering everything through the lens of initiating tests via the client-side data portal to keep behaviors as close to runtime as possible.

@JasonBock
Copy link
Contributor

I guess :). Personally, I'm not that concerned about that, but that's my own view. If I test a controller, I don't necessarily care if any middleware was configured that may change things at runtime. I only care that I can create the controller (with anything passed in on the constructor if needed) and call the method.

@rockfordlhotka rockfordlhotka changed the title Pass custom service provider instance through data portal client to server Enable unit testing of data portal operation methods Jul 30, 2020
@ajj7060
Copy link
Contributor

ajj7060 commented Aug 15, 2020

FWIW, when we're uniting testing our business classes, as assume Csla will do the right thing. We're already configuring a mock container as part of test initialization, and that mock container gets accessed through something like a service locator which is how we're currently resolving dependencies to our BOs (they do contianer.GetInstance for example). As long as we can tell Csla "use this thing to resolve dependencies" easily, that'd be enough I think.

We generally want things go start via the DataPortal.Fetch/FetchAsync so we know everything works as it should. We'll test for things like IsNew is false after a fetch, to make sure we didn't goof and do something outside of a BypassPropertyChecks, for example.

@TheCakeMonster
Copy link
Contributor

I too have been thinking about testability today. I was imagining that we might create a TestDataPortal class to help developers who use CSLA to test their code.

TestDataPortal would implement the new IDataPortal and possibly IChildDataPortal interfaces and allow passing of parameters that are required into the underlying data access method. An alternative is two classes, the second of which implements IChildDataPortal.

These test data portals would do the minimum possible amount of work. However, they do need to continue to support matching of parameters to the most appropriate of available data access methods, plus also providing some support for injection of parameters using the Inject attribute. We could perhaps offer the injection capability using a very lightweight DI that is compatible with testing, by accepting a collection of already instantiated objects as additional parameters.

We want code that calls into the dataportal to be code-compatible, but as simple to use as possible, without the need to bootstrap a full DI container, so that it can be tested. I think that is easier now that DataPortal is instantiated, and implements an interface that is used by consuming code.

The problem with the need to use DI for testing is that it makes it harder to cover different scenarios with the minimum of overhead. Having different tests receive different instances of DAL/repository objects, for example, is more difficult using a complex (and single) DI container.

@rockfordlhotka
Copy link
Member Author

I think the existing CSLA 8+ DI and data portal features support testing adequately, and I'm closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants