Creating Requirements FROM code

Posted by:

|

On:

|

, , ,

Creating user requirements from code is an effective way to communicate what an application is doing to non-technical users.

Traditionally, a product manager defines requirements and gives them to a programmer TO CODE an application.

Now code can be used to generate WHAT REQUIREMENTS HAVE BEEN IMPLEMENTED AND TESTED in an application.

Armed with this knowledge, a product manager now has an opportunity to readily discern how far along an application is in terms of the features it supports, and which supporting user stories have been implemented and are verified to work according to specifications.

It might also be beneficial in situations where there are limited or incomplete requirements, as the results can serve as documentation of the features present.

Here are the steps you need to follow.

  1. Provide a concise overview of the Application Code.
  2. Analyze Test Cases to develop Behavior Driven Development Specifications.
  3. Create a summary of User Stories.

By analyzing application code and related test cases, generative AI can reason what requirements are supported.

In effect, we are reverse engineering code into user requirements.

Note: Google Gemini was used to generate the prompt result examples.

Summarize Application Code

This step introduces application code into a generative AI conversation, providing subsequent prompts with context throughout the “chat”.

Enter the following prompt.

Explain in one paragraph to a non-technical business user what this code does.

Code:

<Insert Application Code here>

Click here for an example of Application Code for you to use.

The result will resemble this.

Analyze Test Cases

This step will analyze test cases’ code to create Behavior Driven Development Specifications.

Most test code tends to focus on unit testing, perhaps as a byproduct of test-driven development (TDD).

Business Driven Development (BDD) is made up of non-technical specifications (features and scenarios).

When a feature is tested it is usually done at a higher level where multiple components of code are combined or composed.

Interestingly, generative AI can deduce features and their scenarios by analyzing unit tests of these individual components, in conjunction with application code.

BDD/TDD example

Let’s consider an example of an application designed to assist grocery cashiers.

Test Unit Cases written using Test-Driven Development (TDD) verify individual tasks.

The following is a test unit case (a procedure) expressed by a programmer after being passed requirements from a product manager or user.

It is in a form a programmer can create code from.

  1. Scan grocery items by UPC
  2. Lookup cost of item
  3. Accept Payment
  4. Print Receipt

Behavior-Driven Development (BDD) describes a feature requirement.

BDD describes a feature (supported by one or more user story scenarios) from the perspective of a user.

Example feature: Allow a customer to checkout and pay for a basket of groceries.

Here are two scenarios:

Example of user story scenarios:

1. Assisted by a grocery employee.

2. Self service by the Customer.

Generate BDD requirements

Enter the following prompt.

Analyze this unit test code and describe using behavior-driven development (BDD) given, when, then concepts what requirements are met.

Elaborate the requirements in detail and explain them as a feature as a user would relate to them.

Unit test code:

<Insert Unit Test Code here>

Click here for an example of Test Code for you to use.

Your result will appear in a similar format.

List Summary of User Stories

Extract user stories from the previous result.

Enter the following prompt.

Extract User Story Context into a table.

The outcome will resemble the following.

Application Code

class LoadMedia {
    enum LoadMediaStatus {
        case ready
        case start
        case inProgress
        case loadError
        case cancelled
        case completedSuccesfully
    }
    var loadMediaStatus: LoadMediaStatus
    var siteLocation: Location?
    
    init(loadMediaStatus: LoadMediaStatus = .ready, siteLocation: Location? = nil) {
        self.loadMediaStatus = loadMediaStatus
        self.siteLocation    = siteLocation
    }
    
    func resetToReady() {
        self.loadMediaStatus = .ready
        self.siteLocation    = nil
    }
    
    func startLoad(_ siteLocation: Location) -> Bool {
        guard self.loadMediaStatus == .ready,
              self.siteLocation == nil else { return false }
        loadMediaStatus     = .start
        self.siteLocation   = siteLocation
        return true
    }
    
    func loadMediaAssets(_ siteLocation: Location) -> Bool {
        guard self.loadMediaStatus == .start,
              self.siteLocation == siteLocation else { return false }
        loadMediaStatus     = .inProgress
        return true
    }
    
    func loadError() {
        loadMediaStatus = .loadError
    }
    
    func cancelLoad() {
        loadMediaStatus = .cancelled
    }
    
    func completedSuccesfully(_ siteLocation: Location) -> Bool {
        guard self.loadMediaStatus == .inProgress,
              self.siteLocation == siteLocation else { return false }
        loadMediaStatus = .completedSuccesfully
        return true
    }
}

Articles

BDD vs. TDD: Differences Explained

https://saucelabs.com/resources/blog/bdd-vs-tdd-differences-explained

Videos

Test Code

final class LoadMediaTests: XCTestCase {
    
    func test_loadMediaStatus_initToReadyFromDefault_readyStateWithUndefinedLocation() throws {
        let resultStatus                = LoadMedia.LoadMediaStatus.ready
        let resultLocation: Location?   = nil
        
        let sut = LoadMedia()
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }

    func test_loadMediaStatus_initToDefinedParameters_definedParametersSet() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.cancelled
        let originalLocation            = LoadMediaTests.validSiteLocation_001
        let resultStatus                = originalStatus
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_resetToReady_fromCancelled_readyStateWithUndefinedLocation() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.cancelled
        let originalLocation            = LoadMediaTests.validSiteLocation_001
        let resultStatus                = LoadMedia.LoadMediaStatus.ready
        let resultLocation: Location?   = nil
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        sut.resetToReady()
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }

    func test_loadMediaStatus_resetToReady_fromCompletedSuccesfully_readyStateWithUndefinedLocation() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.completedSuccesfully
        let originalLocation            = LoadMediaTests.validSiteLocation_001
        let resultStatus                = LoadMedia.LoadMediaStatus.ready
        let resultLocation: Location?   = nil
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        sut.resetToReady()
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }

    func test_loadMediaStatus_resetToReady_fromLoadError_readyStateWithUndefinedLocation() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.loadError
        let originalLocation            = LoadMediaTests.validSiteLocation_001
        let resultStatus                = LoadMedia.LoadMediaStatus.ready
        let resultLocation: Location?   = nil
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation )
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        sut.resetToReady()
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }

    func test_loadMediaStatus_startLoad_fromReady_startLoadWithDefinedLocation_validStart() throws {
        let originalStatus                  = LoadMedia.LoadMediaStatus.ready
        let originalLocation: Location?     = nil
        let changeSiteLocation: Location    = LoadMediaTests.validSiteLocation_001
        let resultStatus                    = LoadMedia.LoadMediaStatus.start
        let resultLocation: Location?       = changeSiteLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let loadStarted = sut.startLoad(changeSiteLocation)
        XCTAssertTrue(loadStarted)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }

    func test_loadMediaStatus_startLoad_notFromReady_invalidStart_stateAndLocationUnchanged() throws {
        let originalStatus                  = LoadMedia.LoadMediaStatus.cancelled
        let originalLocation: Location?     = LoadMediaTests.validSiteLocation_001
        let changeSiteLocation: Location    = LoadMediaTests.validSiteLocation_002
        let resultStatus                    = originalStatus
        let resultLocation: Location?       = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let loadStarted = sut.startLoad(changeSiteLocation)
        XCTAssertFalse(loadStarted)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_inProgress_fromStart_validInProgress() throws {
        let originalStatus                  = LoadMedia.LoadMediaStatus.start
        let originalLocation: Location      = LoadMediaTests.validSiteLocation_001
        let loadMediaSiteLocation: Location = originalLocation
        let resultStatus                    = LoadMedia.LoadMediaStatus.inProgress
        let resultLocation: Location?       = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let inProgress = sut.loadMediaAssets(loadMediaSiteLocation)
        XCTAssertTrue(inProgress)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_inProgress_fromNonStart_inValidInProgress() throws {
        let originalStatus                  = LoadMedia.LoadMediaStatus.cancelled
        let originalLocation: Location      = LoadMediaTests.validSiteLocation_001
        let loadMediaSiteLocation: Location = originalLocation
        let resultStatus           = originalStatus
        let resultLocation: Location?       = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let inProgress = sut.loadMediaAssets(loadMediaSiteLocation)
        XCTAssertFalse(inProgress)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_inProgress_fromStartButWrongLocation_inValidInProgress() throws {
        let originalStatus                  = LoadMedia.LoadMediaStatus.start
        let originalLocation: Location      = LoadMediaTests.validSiteLocation_001
        let loadMediaSiteLocation: Location = LoadMediaTests.validSiteLocation_002
        let resultStatus           = originalStatus
        let resultLocation: Location?       = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let inProgress = sut.loadMediaAssets(loadMediaSiteLocation)
        XCTAssertFalse(inProgress)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_loadError_errorState() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.inProgress
        let originalLocation: Location  = LoadMediaTests.validSiteLocation_001
        let resultErrorStatus           = LoadMedia.LoadMediaStatus.loadError
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        sut.loadError()
        XCTAssertEqual(sut.loadMediaStatus, resultErrorStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_cancelLoad_cancelledState() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.inProgress
        let originalLocation: Location  = LoadMediaTests.validSiteLocation_001
        let resultCancelledStatus       = LoadMedia.LoadMediaStatus.cancelled
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        sut.cancelLoad()
        XCTAssertEqual(sut.loadMediaStatus, resultCancelledStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_successfullyCompleted_fromInProgess_valid() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.inProgress
        let originalLocation: Location  = LoadMediaTests.validSiteLocation_001
        let completedSuccessfullyLocation: Location = originalLocation
        let resultStatus                = LoadMedia.LoadMediaStatus.completedSuccesfully
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let completedSuccessfully = sut.completedSuccesfully(completedSuccessfullyLocation)
        XCTAssertTrue(completedSuccessfully)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_successfullyCompleted_fromInProgess_locationsDontMatch_invalid() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.inProgress
        let originalLocation: Location  = LoadMediaTests.validSiteLocation_001
        let completedSuccessfullyLocation: Location = LoadMediaTests.validSiteLocation_002
        let resultStatus                = originalStatus
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let completedSuccessfully = sut.completedSuccesfully(completedSuccessfullyLocation)
        XCTAssertFalse(completedSuccessfully)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
    
    func test_loadMediaStatus_successfullyCompleted_fromNonInProgess_invalid() throws {
        let originalStatus              = LoadMedia.LoadMediaStatus.start
        let originalLocation: Location  = LoadMediaTests.validSiteLocation_001
        let completedSuccessfullyLocation: Location = originalLocation
        let resultStatus                = originalStatus
        let resultLocation: Location?   = originalLocation
        
        let sut = LoadMedia(loadMediaStatus: originalStatus,
                            siteLocation: originalLocation)
        XCTAssertEqual(sut.loadMediaStatus, originalStatus)
        
        let completedSuccessfully = sut.completedSuccesfully(completedSuccessfullyLocation)
        XCTAssertFalse(completedSuccessfully)
        XCTAssertEqual(sut.loadMediaStatus, resultStatus)
        XCTAssertEqual(sut.siteLocation, resultLocation)
    }
}

extension LoadMediaTests {
    static let validSite_001         = 1
    static let validLocation_001     = 100
    
    static let validSite_002         = 2
    static let validLocation_002     = 200
    
    static let validSiteLocation_001 = Location(site: LoadMediaTests.validSite_001,
                                            location: LoadMediaTests.validLocation_001)
    static let validSiteLocation_002 = Location(site: LoadMediaTests.validSite_002,
                                            location: LoadMediaTests.validLocation_002)
}