I'm setting up a test to test a component and need...
# testing
b
I'm setting up a test to test a component and need to initialize some application wide variables for creating an instance of the .cfc component. I am using
beforeAll()
to instantiate the component. My question is what is recommended for initializing an environment (populating and setting up needed global settings. i.e. found in Application.cfc) in the tests? Example:
Copy code
component extends="testbox.system.BaseSpec" {
	function beforeAll(){

		// I could setup things like this, but is there a better way?
        application.dsn = "myDsn";

		// Is there a way to reference what is set in Application.cfc - Can I just reuse what is already set in Application.cfc? (there are a lot of global vars set...)		
        // should I replicate Application.cfc var settings in the tests/Application.cfc (this feels wrong and maintenance problem prone)


		// instantiate my component with necessary dependencies injected
		myComponent = createObject('component', 'some.cool.Component').init(
			dsn=application.dsn,
			someKey=application.someKey,
			otherInjectedComponent=application.instantiatedOtherComponent,
			//etc....
		)

	}

	function afterAll(){
		// Do I need to do anything here?
	}

	describe("component tests", function() {
		it("can compile", function() {
            // referencing the component instance I created in beforeAll
            expect(myComponent).toBeComponent();
		});
	});
}
s
Does your code under test actually reference
application
scope?
Your instantiation could pass in constants from local scope -- they don't need to be in
application
scope for that...
b
The component is instantiated with a dozen injected dependencies (fyi this is a old code base). It also accesses the database (I don't want to mock this and want to test against data retrieved). My concern is if I create the dependencies locally and they change later, the test will be brittle and break, or will not be testing what I think it is. I was thinking it would be better to use as much of what the real component is using (it pulls these that are set in Application.cfc), so if they change in the future the component in the test will change along with them.
(I am purposefully testing at a high level to start because the app has no tests to begin with and is several years old - so the app is not easily testable)
s
Hmm, that is tricky then...
😢 1
b
But, is it terrible if I do what I was thinking in the comments of my example?
s
I guess could refactor the
application.* = ...;
stuff from
Application.cfc
to an include file and include it in both the
Application.cfc
and the test file. Awful, but it would at least keep things in sync for now.
👀 1
And that would then give you a "target" for refactoring away that include file in favor of using an actual DI framework...
b
If you're not using Coldbox is there a DI framework that is recommended to use for Lucee apps to handle this type of thing?
s
You can use WireBox with a non-ColdBox app. Or you can use DI/1 (again with a non-FW/1 app).
(at work we use DI/1 with ColdBox -- go figure!)
simple smile 1
b
Got it - well that sounds like a rabbit hole I may have to go down then. I'm relatively new to the CFML world, but have seen wirebox mentioned and used as I'm reading documentation and seeing more CMFL code
s
WireBox certainly has more documentation than DI/1, but DI/1 is a lot simpler to use.
b
good to know - I'll check out both then - I'm all for easy to use (please)
s
(full disclaimer: I wrote DI/1 back when I was still active in the CFML community)
b
cool - I bet you'd be a good person to ask about it, then simple smile
s
(I just fixed the doc link in the
di1
repo README)
🤘 1
👀 1
b
I'm going to take a look at this. The only thing I see that might be a problem is that it is "convention based" and the app I'm working with might not follow the conventions assumed.. 🤞 and as for changing it to do that..well you know how old code bases without tests are..
s
You can override the conventions fairly easily -- I'd recommend using the
loadListener
approach and explicitly adding any non-conventional mappings there as you need them. That way you can do things incrementally.
b
Got it, thanks for the tip - maybe there is hope then
🙏🏻 1
g
@Brent Warms my heart to see someone thinking about unit testing - where the level of isolation for that test : is the unit test - as opposed to the class. 🙏🏼
👍 1
b
I've had some experience working on teams with people who really knew what they were doing so I picked up some best practices from them, but I'm always learning and trying to catch up 😅 I got my first successful integration test running in the app I'm working on. Not gonna lie it was kinda brutal. If I can get just some (any!) tests in place then I won't feel so scared to make more changes is the goal. The challenge is going to be to try not to introduce friction or brittle tests because then that might mean I get pushback or the team (including me) will ignore the tests to move forward. So the plan is to stick to testing the stable public APIs and observable behavior as best I can. 🙏
👍🏼 1
g
I can't remember who - Perhaps it was Bob Martin (Uncle Bob) "Imagine you had a button that you could press that told you if all your code worked AND YOU BELEIVED IT"
b
Uncle Bob, Kent Beck, Kent C. Dodds and Vladimir Khorikov (who wrote a great book on unit testing principles) were my main teachers for testing besides colleagues. Yes Bob Martin stressed that the point of having test coverage was to have a big button you can press to tell you if you broke something whenever you make a change to the software. The more trust you have in that button's answer, the more fearless you can be making updates, refactoring to improve the code and adding features. I think it's going to be a long time, if ever to get enough coverage built in to the massive app I'm working with to get to that point but something is better than nothing!
👍🏼 1