question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How should business logic be tested when using decorators?

See original GitHub issue

The approach of using decorators makes it much easier to get started. We have built an app using the examples as a reference implementation. However, we have hit issues with figuring out how to test the business logic wrapped by decorators.

E.g. for AWS Lambda, the example approach is roughly the following:

from slack_bolt import App

app = App(token="xoxb-token", secret="secret")
@app.event({"type": "message", "subtype": None})
def process_message(message, say, request):
    # some more code goes here
    return do_some_logic_based_on_message

def handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

Usually, the problem of isolating external dependencies for the purpose of unit testing can be resolved by splitting the business logic out into a separate class and injecting the Slack client into it, then using mocks for testing like it’s done in the tests. However, since the decorators get applied at module import, and in this case they refer to instance methods on the app object rather than functions in the slack_bolt.app module, the app object needs to be instantiated as a module variable before the process_message function definition.

This makes it difficult to set up a BusinessLogic class and pass a reference to app in the constructor because the methods on the class would be defined before app can be injected into the object instance.

Another option would be to create an instance of a BusinessLogic class after app has been created, then decorate static functions that call methods of that class. The reference to app would have to be passed into the object to allow it to post messages outside of the usual response - e.g. post to channels, etc. For example, like this:

from slack_bolt import App

app = App(token="xoxb-token", secret="secret")
business_logic = BusinessLogic(app)
@app.event({"type": "message", "subtype": None})
def process_message(message, say, request):
    return business_logic.do_some_logic_based_on_message(message)

def handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

Finally, there is always the option of just calling the bare app.* method without the decorators, which avoids some of the extra lines that get added by the decorators:

from slack_bolt import App

app = App(token="xoxb-token", secret="secret")
business_logic = BusinessLogic(app)
# Register handler directly
app.event({"type": "message", "subtype": None}, business_logic.do_some_logic_based_on_message)

def handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

I realise this is a bit rambley, and all of these options seem inelegant and clumsy, so I was wondering if there were some way of testing business logic that I am not seeing.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
seratchcommented, Jun 9, 2021

@latacora-tomekr The main reason why this project’s unit tests have the mock HTTP server is that we want to verify the library’s behavior by performing real HTTP requests, not with only mock objects.

If you would like to have a mock object for WebClient in your tests, you can pass the mocked instance to the App constructor instead. With this approach, you can use any mock library for it. If this approach does not work for you, you can reuse the mock HTTP server in this project for now.

In the long run, we are planning to provide testing tools for Bolt apps (we cannot tell when we can release it, though). If you find obstacles and/or get insights through your Slack app development, sharing your knowledge (in a new issue) would be greatly appreciated.

1reaction
seratchcommented, Mar 3, 2021

👋 Let us know if you need further help here. I will close this issue after waiting for your response for a few more days.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How accurate is "Business logic should be in a service, not in ...
The business logic is in your model. If you want to create "service ... This is the core of the application that we...
Read more >
Leveraging the power of decorators in Python's unit-test ...
Unit testing your code is an industry-wide best-practice to ensure that only good quality, properly tested, stable code is shipped to QA, ...
Read more >
How should I unit test this function to avoid business logic and ...
I have this function that I am unsure how i should unit test. I've read a bit about not having business logic inside...
Read more >
Decorator - Refactoring.Guru
The Decorator lets you structure your business logic into layers, create a decorator for each layer and compose objects with various combinations of...
Read more >
Make tests a part of your app - sobolevn.me
Today I am going to discuss quite a new idea for Python users, an idea of making tests a valuable part of your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found