# Welltested AI

Docs and important FAQs for using Welltested

Welltested is a testing autopilot for Flutter Developers. We've designed the integration to be intuitive for developers and hope this guide will answer all your queries.

> 💡: You can tap on the search field on top right, switch to lens(AI) mode and get direct answers to your questions.


# Set up

### Generate API Key

To get started, first [create an account](https://www.welltested.ai/#pricing) and get an API key.

{% hint style="info" %}
IDE Assist: You may also directly use [*`Set API Key`*](/v1/welltested-ai/ide-assist/commands#set-api-key) to setup the .env file and required dependencies.
{% endhint %}

### Add package from pub.dev

To your pubspec.yaml, add the [welltested](https://pub.dev/packages/welltested/install) package:

```
dependencies:
    welltested: ^1.0.4
```

or run

```
dart pub add add welltested
```

### Setup .env file

Create a `.env` file in root of your project, and add your key received in the email.

```
WELLTESTED_API=YOUR_API_KEY
```

We uses `dotenv` to retrive API Key. Once `.env` file is created, please declare it as an asset in `pubspec.yaml` to make is accessible to the package.

```yaml
assets:
  - .env
```

That's all, we are good to go!&#x20;


# Unit Tests

Unit testing means testing individual units of code for their expected behaviour. Here we simulate every possible scenario of functions and ensure they behave as expected.&#x20;

### Smart tests within minutes

Testing all classes and functions increases code quality and better stability but is a time consuming task when done manually.

Welltested AI allows you to cut this time and easily generate smart and exhaustive unit tests for your entire codebase within minutes.


# Annotation

We provide a seamless workflow integrations for devs by working via code annotations and build\_runner CLI.&#x20;

### `@Welltested()`

You can mark all the methods inside a class to be tested by adding `@Welltested()` annotation.&#x20;

```dart
import 'package:welltested/welltested.dart';

@Welltested()
class Auth {
    Future<void> logInUser() {...}
    Future<void> logOutUser() {...}
}
```

In above example, we will generate unit tests for `logInUser` and `logOutUser` methods of `Auth` class.

> Note: We currently don't support testing top-level (global) functions. Coming soon.

### Testcases

Our testing AI auto decides smart exhaustive test cases for each function based on its use case and contextual code. However you can specify your own custom test cases for a method by adding [`@Testcases()`](/v1/welltested-ai/unit-tests/good-to-knows/custom-testcases) annotation to them.

{% hint style="info" %}
IDE Assist: You can also use [*`Fetch Unit Testcases`*](/v1/welltested-ai/ide-assist/code-actions#fetch-unit-testcases)to review unit test cases and also modify them.
{% endhint %}

### Exclude Methods

By default, all methods in an annotated class are included. To exclude any method from testing, add their name to the `excludedMethods` list in the `Welltested` annotation.

```dart
@Welltested(excludedMethods: ['logOutUser'])
class Auth {
    Future<void> logInUser() {...}
    Future<void> logOutUser() {...}
}
```

{% hint style="info" %}
IDE Assist: You may use [*`Exclude Method`*](/v1/welltested-ai/ide-assist/code-actions#exclude-methods)or [*`Exclude Other Methods`*](/v1/welltested-ai/ide-assist/code-actions#exclude-methods) to run bulk excludes.
{% endhint %}

In the above case, we will not create unit tests for `logOutUser`.


# Generate Tests

{% hint style="info" %}
IDE Assist: Trigger via the IDE command [*`Generate Unit Tests`*](/v1/welltested-ai/ide-assist/commands#generate-unit-tests)
{% endhint %}

To generate tests for your annotated classes, you can use our simple command line across any platform Linux/MacOS and Windows.

```
dart run welltested:ai build
```

The request can take anywhere between 1-10 minutes to generate tests depending upon the number of new methods.&#x20;

After successful run, the tests appear in `/test` directory of you project.

> For convenience, we request you to generate tests in batches for a new project setup. Check [The Batching Rule](/v1/welltested-ai/unit-tests/good-to-knows/the-batching-rule)

### Maintaining Tests

If you make any changes to your existing functions or add new methods, you can re run the same commands and the AI will smartly update the tests.&#x20;


# Save Tests

{% hint style="info" %}
IDE Assist: Trigger via the IDE command [*`Save Unit Tests`*](/v1/welltested-ai/ide-assist/commands#save-unit-tests)
{% endhint %}

Whenever you makes fixes to the tests or annotated classes, it's required to save those updates on our systems so we don't overwrite them with our existing cache.

\
To save your tests, run this command:

```
dart run welltested:save build
```

### Benefits of saving tests:

1. **Avoid Mistake Repetition:** If you fix a mistake in our generated tests and save it on the system, we tend to learn from your fixes and not repeat them again.
2. **Match your code style:** With more examples of the code style and pattern you like, we create tuned tests for you.&#x20;


# Good to Knows


# Custom Testcases

We provide you with the option to specify your own testcases for any functions. This helps:&#x20;

* teams following Test Driven Development
* developers having complex functions in their codebase with quirky cases.
* or to limit the scope of testing of a certain function.

One can specify these cases in natural language with `@Testcases` annotation

```dart
@Welltested()
class Auth {
    @Testcases(['Throws exception when email is empty'])
    Future<User> logInUser() {...}
}
```


# Regenerate Bad Tests

In case, you're not satisfied with a generated test for a method, you can ask for a regeneration of it.&#x20;

Follow the steps:

1. Delete the corresponding file of the test.
2. Run the generation `dart run welltested:ai build`

> 💡: If you've multiple bad tests with same mistakes, you can fix one of them and save it back to the system. Then regenerate the others again and the mistake will be automatically fixed.


# The Batching Rule

When adding Welltested to an existing project, it's recommended to generate tests in the batches.&#x20;

This gives Welltested the opportunity to learn your code style and any mistakes it's making and provide better subsequent results.

### Batching procedure

Start out with 3 classes that are different with each other in terms of functionality and dependencies used. Fix any syntax issues in generated tests and save them to the system.&#x20;

Repeat the process with 5, 7, 10 and more and you'll see that we start performing better with each iterations

> 💡: Initially choose classes with 1-3 methods or else add the other methods to `excludeMethods` to avoid manually fixing a large number of tests.&#x20;


# FAQs

Common Questions and Answers


# Paid Plans

### Is the individual plan completely free?

Yes, welltested's free plan is for our community users for their personal projects, non proprietary work etc with an agreement to share data for improving our models. This is a symbiotic relationship allows everyone to build welltested apps while benefiting other users from their contributions.&#x20;

### When should one not use a free plan?

If you plan to use welltested for any work related projects, productions apps, etc, we recommend going for a paid plan to keep your codebase completely private.&#x20;


# Code Privacy

### What parts of my code are collected?

We only analyze code and context required to generate unit test cases, i.e. the classes marked for testing and their dependencies. ‍We also save the generated tests and the updates made by you in the tests.

### Is my code used for training?

If you're a startup or enterprise customer, your data is fully private and we don't use it to improve our models.&#x20;

For individual users, we use the generated test data to enhance our public models and improve the tests quality for all users.

### I accidentally used my individual account for my work project. What can I do?

Don't stress, in most cases we run scrutiny before using collected code for training so nothing sensitive will be likely compromised. Also, you can request deletion of your existing data for any reason regardless of your plan. No questions asked! Please reach us on <teams@welltested.ai>.&#x20;


# Support

### I'm having trouble with `welltested` package. Where can I get help?

If you're facing any issues related to installing, running and using the  `welltested` pub.dev package, please [file issue on the github repository.](https://github.com/Welltested-AI/welltested/issues)&#x20;

### I'd like a demo on using welltested, is that possible?

Of course, you can either email us to <teams@welltested.ai> or else book a direct call with the founding team via this [calendly](https://calendly.com/samyakkkk/30min).

{% embed url="<https://calendly.com/samyakkkk/30min>" %}


# IDE Assist

VSCODE and Intelli Extensions

Welltested is designed with the spirit to support every function right from the code and CLI operations.

The IDE extensions carrying the same motivation, provide powerful tools to easily trigger these operations.


# Commands

### SET API KEY

Save setup hassle of setting up a installing required dependencies and adding your API keys to .env.

{% tabs %}
{% tab title="VSCode" %}

1. Run the command `Welltested: Save API Key`
2. Paste your API key in the input window.

The package will auto setup required dependencies and env file.

<figure><img src="https://content.gitbook.com/content/tAnLSsYAHhs9z40FBfs5/blobs/hfLeDTi2eLNVU1v7zM7k/Easy%20Setup.gif" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Intellij" %}

{% endtab %}
{% endtabs %}

### Generate Unit Tests

Use to trigger the generate CLI command. Generates unit tests for all annotated classes.

{% tabs %}
{% tab title="VSCode" %}

1. Run command `Welltested: Generate Unit Tests`

You'll see the CLI command triggered. The tests will appear in the `/test` folder once the operation finishes.

<figure><img src="https://content.gitbook.com/content/tAnLSsYAHhs9z40FBfs5/blobs/3d0Ss21BiGqcECp7Wv2z/Generate%20Unit%20Tests.gif" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="IntelliJ" %}

{% endtab %}
{% endtabs %}

### Save Unit Tests

Use to trigger the save CLI command. Save updates to existing unit tests and their corresponding code files to the backend.

{% tabs %}
{% tab title="VSCode" %}

1. Run the command `Welltested: Save Unit Tests`

You'll see the CLI command triggered. All updates will be saved to the backend.

<figure><img src="https://content.gitbook.com/content/tAnLSsYAHhs9z40FBfs5/blobs/mp1FibmpCN2fNm30aju6/Save%20Unit%20Tests.gif" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="IntelliJ" %}

{% endtab %}
{% endtabs %}


# Code Actions

### Exclude Methods

`Exclude Method` puts the selected method in *`excludedMethods`* marking it to not be tested.

`Exclude Other Methods` excludes all other methods in the class marking only your selected function to be tested.

{% tabs %}
{% tab title="VSCode" %}

1. Tapping on a function you'll see a 💡 bulb. You can also use CMD + .
2. A bulb menu with both exclude methods should appear.
3. Tap on the exclude method option of your choice.

<figure><img src="https://content.gitbook.com/content/tAnLSsYAHhs9z40FBfs5/blobs/C501nka9NdgOnI4ngiys/Exclude%20Methods.gif" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="IntelliJ" %}

{% endtab %}
{% endtabs %}

### Fetch Unit Testcases

Fetches a list of smart and exhaustive testcases for your method which you can review and modify.&#x20;

{% tabs %}
{% tab title="VSCode" %}

1. Tapping on a function you'll see a 💡 bulb. You can also use CMD + .
2. A bulb menu with both exclude methods should appear.
3. Tap on the Fetch Unit Testcases option.

<figure><img src="https://content.gitbook.com/content/tAnLSsYAHhs9z40FBfs5/blobs/3d0Ss21BiGqcECp7Wv2z/Generate%20Unit%20Tests.gif" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="IntelliJ" %}

{% endtab %}
{% endtabs %}


# Testability Guidelines

Any code isn’t testable. This document covers the main points to ensure your codebase is ready to accommodate and benefit from unit tests.

## What makes a code testable?

Code is considered testable when it is designed in a way that allows automated tests to be easily created and run. This means that the code should be:

1. Modular, with clear separation of concerns and dependencies.
2. Passing dependencies from constructors to allow mocking.
3. Following Single Responsibility Principle (SRP).

When code is testable, it allows developers to catch bugs and issues early on in the development process and ensure that changes to the code don't introduce new problems. This can save time and resources in the long run, as it helps to prevent issues from appearing in production and reduces the need for manual testing.

## Practices to follow

#### 1. Use Dependency Injection

Dependency injection is a design pattern that allows us to inject dependencies into our code. This makes it easier to write unit tests because we can replace dependencies with mocks or stubs. In Flutter, we can use the `provider` package to implement dependency injection. Here is an example:

```dart
class UserService {
  final HttpClient httpClient;

  UserService(this.httpClient);

  Future<User> getUser(String id) async {
    final response = await httpClient.get('/users/$id');
    return User.fromJson(response.data);
  }
}
```

In this example, we are injecting an `HttpClient` dependency into the `UserService`. This makes it easier to test because we can replace the `HttpClient` with a mock during testing.

#### 2. Use Separation of Concerns

Write small and focused functions: Functions that do one thing and do it well are easier to test. Avoid large functions that perform multiple tasks.

```dart
bool isEmailValid(String email) {
  final regex = RegExp(r'^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$');
  return regex.hasMatch(email);
}
```

#### 3. Avoid global and static states

Global state makes it difficult to test your code in isolation because changes in one part of the code can affect other parts. In addition to that, they can’t be mocked directly.

```dart
class Counter {
  static int count = 0;

  static void increment() {
    count++;
  }
}

```

#### 4. Don't call another function within the same class.

Writing unit tests for functions calling other functions within the same class can become messy and difficult to manage, especially because you can't mock or simulate their output.&#x20;

```dart
class Example {
  int add(int a, int b) {
    return a + b;
  }

  int multiply(int a, int b) {
    int result = 0;
    for (int i = 0; i < a; i++) {
      result = add(result, b);
    }
    return result;
  }
}
```

Suppose `multiply` is the main function, while `add` is the sub-function being called inside `multiply`. It becomes hard to write a test for the `multiply` function on its own, because it depends on the `add` function's implementation.

Using Dependency Injection:

```dart
class Adder{
  int add(int a, int b) {
    return a + b;
  }
}

class BetterExample {
  final Adder _adder;
  
  BetterExample(this._adder);

  int multiply(int a, int b) {
    int result = 0;
    for (int i = 0; i < a; i++) {
      result = _adder.add(result, b);
    }
    return result;
  }
}
```

Now `multiply` depends on an instance of the `Adder` class, which can be easily mocked for testing. The test for the `multiply` function can be written without having to worry about testing the `add` function.

#### 5. Use SOLID Principles

SOLID principles are a set of software design principles that help us to write code that is maintainable, scalable and testable. A summary:

| Single Responsibility Principle (SRP) | A class/method should have only one reason to change.                                          |
| ------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Open/Closed Principle (OCP)           | A class should be open for extension but closed for modification.                              |
| Liskov Substitution Principle (LSP)   | Subtypes must be substitutable for their base types.                                           |
| Interface Segregation Principle (ISP) | Clients should not be forced to depend on methods they do not use.                             |
| Dependency Inversion Principle (DIP)  | High-level modules should not depend on low-level modules. Both should depend on abstractions. |

For more details, please read [Unit Testing Introduction](https://www.toptal.com/flutter/unit-testing-flutter).


# Documentation

Docs and important FAQs for using Welltested

Welltested is a testing autopilot for Flutter Developers. We've designed the integration to be intuitive for developers and hope this guide will answer all your queries.

> 💡: You can tap on the search field on top right, switch to lens(AI) mode and get direct answers to your questions.


# Set up

### Generate API Key

To get started, [create an account](https://www.welltested.ai/#pricing) and get your API key.

### Setup .env file

Create a `.env` file in root of your project, and add your API key received in the email.

```
WELLTESTED_API=YOUR_API_KEY
```

### Add dependencies

To your pubspec.yaml, add the [welltested\_annotation](https://pub.dev/packages/welltested_annotation) package and other optional dependencies as below:

```
dependencies:
    welltested_annotation: ^1.0.1

dev_dependencies:
    // to generate mocks 
    mockito:
    build_runner:
```

then, activate the welltested CLI globally using:

```
dart pub global activate welltested
```

Once you have activated welltested CLI. Run the following command to test if the welltested  command is working in your CLI:

```bash
welltested --help
```

{% hint style="info" %}
**Note**: Upon running `welltested --help`, if your CLI complains **welltested: command not found.** Please check if your dart pub setup is complete. For detailed instructions, refer to the [Dart documentation](https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path).
{% endhint %}

That's all, we're good to go!


# Unit Tests

Unit testing means testing individual units of code for their expected behaviour. Here we simulate every possible scenario of functions and ensure they behave as expected.&#x20;

### Smart tests within minutes

Testing all classes and functions increases code quality and better stability but is a time consuming task when done manually.

Welltested AI allows you to cut this time and easily generate smart and exhaustive unit tests for your entire codebase within minutes.


# Annotation

Welltested uses annotations to mark the methods in your code to be tested. You can mark classes, extensions or top-level functions.

### `@Welltested()`

You can mark the methods inside a class or an extension to be tested by adding `@Welltested()` annotation to the parent class/extension.&#x20;

```dart
import 'package:welltested_annotation/welltested_annotation.dart';

@Welltested()
class Auth {
    Future<void> logInUser() {...}
    Future<void> logOutUser() {...}
}
```

In the above example, we will generate unit tests for `logInUser` and `logOutUser` methods of `Auth` class.

**Annotating Top Level Functions:**

If you've top-level functions in your code that do not belong to a class or extension method, you can annotate them directly.&#x20;

```dart
import 'package:welltested_annotation/welltested_annotation.dart';

@Welltested()
double cartCalculator(List<CartItem> items, double tax) {...} // top-level function

```

### Testcases

Our testing AI examines and self creates smart exhaustive test cases for each function based on its use case and contextual code. However, you can specify your own custom test cases for a method by adding [`@Testcases()`](/documentation/unit-tests/good-to-knows/custom-testcases) annotation to them.

```dart
class Auth {
  @Testcases([
    "Ensure token is saved if login is successful",
    "Ensure existing token is cleared if login fails"
  ])
  Future<void> logInUser() {...}
  Future<void> logOutUser() {...}
}
```

### Exclude Methods

By default, all methods in an annotated class are included. To exclude any method from testing, add their name to the `excludedMethods` list in the `Welltested` annotation.

```dart
@Welltested(excludedMethods: ['logOutUser'])
class Auth {
    Future<void> logInUser() {...}
    Future<void> logOutUser() {...}
}
```

In the above case, we will not create unit tests for `logOutUser`.


# Generate Tests

To generate tests for your annotated classes, run the command:

```
welltested generate unit
```

The execution can take anywhere between 1-10 minutes to generate tests depending upon the number of methods.&#x20;

After a successful run, the tests appear in `/test` directory of your project.

> In the beginning, we recommend you generate tests in batches for the best results. Check [The Batching Rule](/documentation/unit-tests/good-to-knows/the-batching-rule)

### Scope Arguments \[Optional]

You may use the scope arguments to generate tests only for a certain file, class and/or method.

Examples:

`welltested generate unit -f lib/login/repository.dart`

`welltested generate unit -c AccountsRepository -m getAllTransactions`

*-f, -c or -m* can all be used independently of each other and will narrow the scope accordingly.

### Maintaining Tests

If you make any changes to your existing functions or add new methods, you can run the same commands and welltested will auto-update the tests.&#x20;

We ensure that your tests always stay up to date with minimal maintenance efforts 🙌🏼


# Save Tests

Whenever you make changes to the tests or your code, it's required to save those updates to welltested so we don't overwrite them from the cache.

\
To save your changes, run the command:

```
welltested save unit
```

### Saving tests help:

1. **Reduce Errors:** If you rectify a mistake in our generated tests and save the fix, we tend to learn from your fixes and not repeat the mistakes again.
2. **Learn your code style:**  With more examples of your code style and pattern, we create better tests tuned for you.&#x20;


# Good to Knows


# Custom Testcases

We provide you with the option to specify your own test cases for any functions. This helps:&#x20;

* teams following Test Driven Development
* developers having complex functions in their codebase with quirky cases.
* or to limit the scope of testing of a certain function.

One can specify these cases in natural language with `@Testcases` annotation

```dart
@Welltested()
class Auth {
    @Testcases(['Throws exception when email is empty'])
    Future<User> logInUser() {...}
}
```


# Regenerate Bad Tests

If you're not satisfied with a generated test, you may regenerate it. Follow the steps:

1. Delete the corresponding test file.
2. Run  `welltested generate unit`&#x20;

> 💡: If you've multiple bad tests with similar mistakes, you can fix one of them and save it. Then regenerate the others again and the mistake will be automatically fixed.&#x20;


# The Batching Rule

When getting started with welltested, the first few test generations may have syntax errors. However as you fix the issues and save the tests, welltested starts learning from them and provides better subsequent results.

Hence we strongly recommend creating tests in batches of 1, 3, 5, 7 and 10.

### Batching procedure

Start out with 3 methods that are different from each other in terms of syntax and dependencies used.  Fix any syntax issues in generated tests and save them to the system.&#x20;

Repeat the process with 5, 7, 10 and more and you'll see that we start performing better with each iteration.

> 💡: To select specific methods, use [`excludeMethods`](/documentation/unit-tests/annotation#exclude-methods) or [scoped arguments](/documentation/unit-tests/generate-tests#scope-arguments-optional).


# FAQs

Common Questions and Answers


# Paid Plans

### Is the individual plan completely free?

Yes, the free plan is good for our users for their personal projects, non-proprietary work etc with an agreement to share data for improving our models. This symbiotic relationship allows everyone to build welltested apps while benefiting other users from their contributions.&#x20;

### When should one not use a free plan?

If you plan to use welltested for any work-related projects, we recommend going for a paid plan to keep your codebase completely private.&#x20;


# Code Privacy

### What parts of my code are collected?

We only analyze the code and context required to generate unit tests, i.e. the classes annotated for testing and their dependencies. ‍We also save the generated tests and the updates made by you in the tests.

### Is my code used for training?

If you're a startup or enterprise customer, your data is fully private and we don't use it to improve our models.&#x20;

For individual users, we use the generated test data to enhance our public models and improve the test quality for all users.

### I accidentally used my individual account for my work project. What can I do?

Don't stress, in most cases, we run scrutiny before using collected code for training so nothing sensitive will be likely compromised. Also, you can request the deletion of your existing data for any reason regardless of your plan. No questions asked! Please reach us at <team@welltested.ai>.&#x20;


# Support

### I'm having trouble with `welltested` package. Where can I get help?

If you're facing any issues related to installing, running and using the  `welltested` pub.dev package, please [file issue on the github repository.](https://github.com/Welltested-AI/welltested/issues)&#x20;

### I'd like a demo on using welltested, is that possible?

Of course, you can either email us to <team@welltested.ai> or else book a direct call with the founding team via this [calendly](https://calendly.com/samyakkkk/30min).

{% embed url="<https://calendly.com/samyakkkk/30min>" %}


# Using with FVM

For users who use https\://fvm.app/ to manage flutter versions

FVM uses its own version which welltested activated in global CLI won't know. For a simple solution:

Add welltested to dev dependencies.

```yaml
dev_dependencies:
    welltested: 
```

Instead of using welltested commands directly, prefix them with `dart run` . For example:

```
dart run welltested generate unit
```


# Testability Guidelines

Any code isn’t testable. This document covers the main points to ensure your codebase is ready to accommodate and benefit from unit tests.

## What makes a code testable?

Code is considered testable when it is designed in a way that allows automated tests to be easily created and run. This means that the code should be:

1. Modular, with clear separation of concerns and dependencies.
2. Passing dependencies from constructors to allow mocking.
3. Following Single Responsibility Principle (SRP).

When code is testable, it allows developers to catch bugs and issues early on in the development process and ensure that changes to the code don't introduce new problems. This can save time and resources in the long run, as it helps to prevent issues from appearing in production and reduces the need for manual testing.

## Practices to follow

#### 1. Use Dependency Injection

Dependency injection is a design pattern that allows us to inject dependencies into our code. This makes it easier to write unit tests because we can replace dependencies with mocks or stubs. In Flutter, we can use the `provider` package to implement dependency injection. Here is an example:

```dart
class UserService {
  final HttpClient httpClient;

  UserService(this.httpClient);

  Future<User> getUser(String id) async {
    final response = await httpClient.get('/users/$id');
    return User.fromJson(response.data);
  }
}
```

In this example, we are injecting an `HttpClient` dependency into the `UserService`. This makes it easier to test because we can replace the `HttpClient` with a mock during testing.

#### 2. Use Separation of Concerns

Write small and focused functions: Functions that do one thing and do it well are easier to test. Avoid large functions that perform multiple tasks.

```dart
bool isEmailValid(String email) {
  final regex = RegExp(r'^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$');
  return regex.hasMatch(email);
}
```

#### 3. Avoid global and static states

Global state makes it difficult to test your code in isolation because changes in one part of the code can affect other parts. In addition to that, they can’t be mocked directly.

```dart
class Counter {
  static int count = 0;

  static void increment() {
    count++;
  }
}

```

#### 4. Don't call another function within the same class.

Writing unit tests for functions calling other functions within the same class can become messy and difficult to manage, especially because you can't mock or simulate their output.&#x20;

```dart
class Example {
  int add(int a, int b) {
    return a + b;
  }

  int multiply(int a, int b) {
    int result = 0;
    for (int i = 0; i < a; i++) {
      result = add(result, b);
    }
    return result;
  }
}
```

Suppose `multiply` is the main function, while `add` is the sub-function being called inside `multiply`. It becomes hard to write a test for the `multiply` function on its own, because it depends on the `add` function's implementation.

Using Dependency Injection:

```dart
class Adder{
  int add(int a, int b) {
    return a + b;
  }
}

class BetterExample {
  final Adder _adder;
  
  BetterExample(this._adder);

  int multiply(int a, int b) {
    int result = 0;
    for (int i = 0; i < a; i++) {
      result = _adder.add(result, b);
    }
    return result;
  }
}
```

Now `multiply` depends on an instance of the `Adder` class, which can be easily mocked for testing. The test for the `multiply` function can be written without having to worry about testing the `add` function.

#### 5. Use SOLID Principles

SOLID principles are a set of software design principles that help us to write code that is maintainable, scalable and testable. A summary:

| Single Responsibility Principle (SRP) | A class/method should have only one reason to change.                                          |
| ------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Open/Closed Principle (OCP)           | A class should be open for extension but closed for modification.                              |
| Liskov Substitution Principle (LSP)   | Subtypes must be substitutable for their base types.                                           |
| Interface Segregation Principle (ISP) | Clients should not be forced to depend on methods they do not use.                             |
| Dependency Inversion Principle (DIP)  | High-level modules should not depend on low-level modules. Both should depend on abstractions. |

For more details, please read [Unit Testing Introduction](https://www.toptal.com/flutter/unit-testing-flutter).


