如何在不新增参数的情况下用QUnit测试SAPUI5控制器?
Hey there! I see you're running into an issue where your QUnit test throws an error because rowData[0] is undefined when executing data = dataConnector.getContractHistoryData(rowData[0].M_BIC_B_NSN). You tried adding a testObj parameter to initController but don't like that approach—totally understandable, since modifying production code just for tests isn't ideal. Let's fix this properly by mocking your dependencies instead.
Root Cause of the Error
In your production code, rowData comes from ViewControls.getRowData(). In your test, you defined a local getRowData stub, but you didn't actually replace the ViewControls.getRowData method that your controller uses. That means when the controller runs, rowData ends up as undefined, hence the rowData[0] error.
Step-by-Step Solution
Instead of modifying initController, we'll use Sinon to stub the static methods on ViewControls and dataConnector that your controller depends on. Here's how to adjust your test:
- Stub
ViewControls.getRowData()to return your test row data, so the controller gets the expected value forrowData. - Stub
ViewControls.detailsViewControls.apply(this)to return your mockdetailsControlsobject—this replaces the need for thetestObjparameter. - Stub
dataConnector.getContractHistoryData()to avoid making real backend calls during testing. - Stub
ViewControls.setPriceChangeAccepted()since it's a static method called ininitController.
Modified Test Code
QUnit.test("initController setup", function(assert) { // Arrange var setEnabled = sinon.spy(); var setValue = sinon.spy(); var getValue = sinon.spy(); var setVisible = sinon.spy(); var setVisibleRowCount = sinon.spy(); var setValueState = sinon.spy(); // Test row data var testRowData = [ { BAWAEFFDT: "20150213", BZZPIIN: "SPE5E967V1599", CAGE: "4R840", CUR_QTY: "20", FAIR_MKT_PRICE: "160.00", INDEX: "3", M_BIC_B_NSN: "306018040", NETPRICE: "150.99", ORDER_QUAN: "35", PERC_DIFF: "20", PROPOSED_PRICE: "25.99", PriceReasonableCode: "BG", VENDOR: "STAMCO." } ]; // Mock detailsControls var mockDetailsControls = { summaryTable: { setVisibleRowCount: setVisibleRowCount }, evalPriceButton: { setEnabled: setEnabled }, nextBtn: { setVisible: setVisible, setEnabled: setEnabled }, prevBtn: { setVisible: setVisible, setEnabled: setEnabled }, dropDown: { getValue: getValue, setValue: setValue }, acceptBtn: { setEnabled: setEnabled }, awardQuantity: { setValue: setValue, getValue: getValue, setEnabled: setEnabled, setValueState: setValueState }, missingFieldError: { setVisible: setVisible }, invalidFieldError: { setVisible: setVisible }, invalidDateError: { setVisible: setVisible }, buyQuantity: { setEnabled: setEnabled, setValue: setValue, setValueState: setValueState }, priorUnitPrice: { setEnabled: setEnabled, setValue: setValue, setValueState: setValueState }, proposedUnitPrice: { setEnabled: setEnabled, setValue: setValue, setValueState: setValueState }, awardDate: { setEnabled: setEnabled, setValue: setValue, setValueState: setValueState } }; // Stub all dependencies sinon.stub(ViewControls, "getRowData").returns(testRowData); sinon.stub(ViewControls, "detailsViewControls").returns(mockDetailsControls); sinon.stub(ViewControls, "setPriceChangeAccepted"); // Stub to prevent actual execution sinon.stub(dataConnector, "getContractHistoryData").returns([]); // Mock empty history data or test data // Act detailsController.initController(); // No need to pass testObj anymore! // Assert (add your assertions here, e.g.:) assert.ok(setEnabled.calledWith(true), "evalPriceButton should be enabled"); assert.ok(setEnabled.calledWith(false), "acceptBtn should be disabled"); assert.ok(ViewControls.setPriceChangeAccepted.calledWith(false), "Price change accepted should be set to false"); // Cleanup: Restore stubs after test ViewControls.getRowData.restore(); ViewControls.detailsViewControls.restore(); ViewControls.setPriceChangeAccepted.restore(); dataConnector.getContractHistoryData.restore(); });
Key Changes Explained
- Stubbed
ViewControls.getRowData(): Now the controller gets your testtestRowDatainstead ofundefined, sorowData[0]exists. - Stubbed
ViewControls.detailsViewControls: This replaces the need for thetestObjparameter—your mock controls are used automatically via the||fallback in the original code. - Stubbed
dataConnector.getContractHistoryData(): Prevents real API calls and returns predictable test data. - Cleanup: Restoring stubs ensures they don't affect other tests.
This approach keeps your production code untouched while making your test reliable and maintainable.
内容的提问来源于stack exchange,提问作者polaris

