Repository: scikit-activeml/scikit-activeml (language: python)

Issue:
Move `clf` from `__init.py__` to `query`.
- [x] `_expected_error_reduction.py`
- [x] `_uncertainty_sampling.py`
- [x] `_probal.py`
- [x] `_epistemic_uncertainty_sampling.py`
- [x] `_four_ds.py`
- [x] `_qbc.py`  

See branch [clf_in_query](https://github.com/scikit-activeml/scikit-activeml/tree/clf_in_query)


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0d8bb8d2..cede3b90 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -38,7 +38,7 @@ jobs:
       run: |
         pip install pytest
         pip install pytest-cov
-        pytest --cov=./ --cov-report=xml
+        pytest --cov=./skactiveml  --cov-report=xml
     - name: "Upload coverage to Codecov"
       uses: codecov/codecov-action@v1
       with:
diff --git a/README.rst b/README.rst
index e1f55b47..151ffba0 100644
--- a/README.rst
+++ b/README.rst
@@ -35,29 +35,32 @@ The easiest way of installing scikit-activeml is using ``pip``   ::
 Example
 =======
 
-The following code implements an Active Learning Cycle with 20 iterations using a Logistic Regression Classifier and Uncertainty Sampling. To use other classifiers, you can simply wrap classifiers from ``scikit-learn`` or use classifiers provided by ``scikit-activeml``. Note that the main difficulty using active learning with ``scikit-learn`` is the ability to handle unlabeled data which we denote as a specific value (``MISSING_LABEL``) in the label vector ``y_true``. More query strategies can be found in the documentation.     ::
+The following code implements an active learning cycle with 20 iterations using a logistic regression classifier and uncertainty sampling. To use other classifiers, you can simply wrap classifiers from ``scikit-learn`` or use classifiers provided by ``scikit-activeml``. Note that the main difficulty using active learning with ``scikit-learn`` is the ability to handle unlabeled data, which we denote as a specific value (``MISSING_LABEL``) in the label vector ``y``. More query strategies can be found in the documentation.     ::
 
     import numpy as np
     from sklearn.linear_model import LogisticRegression
     from sklearn.datasets import make_classification
     from skactiveml.pool import UncertaintySampling
-    from skactiveml.utils import is_unlabeled, MISSING_LABEL
+    from skactiveml.utils import unlabeled_indices, MISSING_LABEL
     from skactiveml.classifier import SklearnClassifier 
 
+    # Generate data set.
     X, y_true = make_classification(random_state=0)
     y = np.full(shape=y_true.shape, fill_value=MISSING_LABEL)
 
-    clf = SklearnClassifier(LogisticRegression(),
-                            classes=np.unique(y_true))
-    qs = UncertaintySampling(clf, method='entropy')
+    # Create classifier and query strategy.
+    clf = SklearnClassifier(LogisticRegression(), classes=np.unique(y_true))
+    qs = UncertaintySampling(method='entropy')
 
+    # Execute active learning cycle.
     n_cycles = 20
     for c in range(n_cycles):
-         unlbld_idx = np.where(is_unlabeled(y))[0]
+         clf.fit(X, y)
+         unlbld_idx = unlabeled_indices(y)
          X_cand = X[unlbld_idx]
-         query_idx = unlbld_idx[qs.query(X_cand=X_cand, X=X, y=y)]
+         query_idx = unlbld_idx[qs.query(X_cand=X_cand, clf=clf)]
          y[query_idx] = y_true[query_idx]
-         clf.fit(X, y)
+    print(f'Accuracy: {clf.fit(X, y).score(X, y_true)}')
 
 Development
 ===========
@@ -68,5 +71,5 @@ More information are available in the `Developer's Guide
 Documentation
 =============
 
-The doumentation is available here:
+The documentation is available here:
 https://scikit-activeml.readthedocs.io
diff --git a/skactiveml/base.py b/skactiveml/base.py
index 8de5135d..67ecce05 100644
--- a/skactiveml/base.py
+++ b/skactiveml/base.py
@@ -2,7 +2,7 @@
 from abc import ABC, abstractmethod
 
 import numpy as np
-from sklearn.base import BaseEstimator, ClassifierMixin, clone
+from sklearn.base import BaseEstimator, ClassifierMixin
 from sklearn.metrics import accuracy_score
 from sklearn.utils import check_array, check_consistent_length
 from sklearn.utils.multiclass import type_of_target
@@ -27,7 +27,7 @@ def __init__(self, random_state=None):
     def query(self, *args, **kwargs):
         """Determines the query for active learning based on input arguments.
         """
-        return NotImplemented
+        raise NotImplementedError
 
 
 class SingleAnnotPoolBasedQueryStrategy(QueryStrategy):
@@ -69,7 +69,7 @@ def query(self, X_cand, *args, batch_size=1, return_utilities=False,
             used for selecting the first sample (with index `query_indices[0]`)
             of the batch.
         """
-        return NotImplemented
+        raise NotImplementedError
 
     def _validate_data(self, X_cand, return_utilities, batch_size,
                        random_state, reset=True, **check_X_cand_params):
@@ -177,7 +177,8 @@ def query(self, X_cand, *args, A_cand=None, batch_size=1,
             `utilities[0, :, j]` indicates the utilities used for selecting
             the first sample-annotator pair (with indices `query_indices[0]`).
         """
-        return NotImplemented
+        raise NotImplementedError
+
 
 class SkactivemlClassifier(BaseEstimator, ClassifierMixin, ABC):
     """SkactivemlClassifier
@@ -209,8 +210,9 @@ class SkactivemlClassifier(BaseEstimator, ClassifierMixin, ABC):
         Cost matrix with C[i,j] indicating cost of predicting class classes_[j]
         for a sample of class classes_[i].
     """
-    def __init__(self, classes, missing_label=MISSING_LABEL, cost_matrix=None,
-                 random_state=None):
+
+    def __init__(self, classes=None, missing_label=MISSING_LABEL,
+                 cost_matrix=None, random_state=None):
         self.classes = classes
         self.missing_label = missing_label
         self.cost_matrix = cost_matrix
@@ -237,7 +239,7 @@ def fit(self, X, y, sample_weight=None):
         self: SkactivemlClassifier,
             The SkactivemlClassifier is fitted on the training data.
         """
-        return NotImplemented
+        raise NotImplementedError
 
     def predict_proba(self, X):
         """Return probability estimates for the test data X.
@@ -253,7 +255,7 @@ def predict_proba(self, X):
             The class probabilities of the test samples. Classes are ordered
             according to 'classes_'.
         """
-        return NotImplemented
+        raise NotImplementedError
 
     def predict(self, X):
         """Return class label predictions for the test samples X.
@@ -300,7 +302,7 @@ def score(self, X, y, sample_weight=None):
         y_pred = self._le.transform(self.predict(X))
         return accuracy_score(y, y_pred, sample_weight=sample_weight)
 
-    def _validate_data(self, X, y, sample_weight):
+    def _validate_data(self, X, y, sample_weight=None):
         # Check common classifier parameters.
         check_classifier_params(self.classes, self.missing_label,
                                 self.cost_matrix)
@@ -422,7 +424,7 @@ def predict_freq(self, X):
             The class frequency estimates of the test samples 'X'. Classes are
             ordered according to attribute 'classes_'.
         """
-        return NotImplemented
+        raise NotImplementedError
 
     def predict_proba(self, X):
         """Return probability estimates for the test data X.
@@ -446,7 +448,7 @@ def predict_proba(self, X):
         P[normalizer == 0, :] = [1 / len(self.classes_)] * len(self.classes_)
         return P
 
-    def _validate_data(self, X, y, sample_weight):
+    def _validate_data(self, X, y, sample_weight=None):
         X, y, sample_weight = super()._validate_data(X, y, sample_weight)
         # Check class prior.
         self.class_prior_ = check_class_prior(self.class_prior,
@@ -454,12 +456,13 @@ def _validate_data(self, X, y, sample_weight):
         return X, y, sample_weight
 
 
-class AnnotModelMixing(ABC):
-    """AnnotModelMixing
+class AnnotModelMixin(ABC):
+    """AnnotModelMixin
 
     Base class of all annotator models estimating the performances of
     annotators for given samples.
     """
+
     @abstractmethod
     def predict_annot_proba(self, X):
         """Calculates the probability that an annotator provides the true label
@@ -476,4 +479,4 @@ def predict_annot_proba(self, X):
             `P_annot[i,l]` is the probability, that annotator `l` provides the
             correct class label for sample `X[i]`.
         """
-        return NotImplemented
\ No newline at end of file
+        raise NotImplementedError
diff --git a/skactiveml/classifier/_logistic_regression.py b/skactiveml/classifier/_logistic_regression.py
index b021eac8..29b10bcf 100644
--- a/skactiveml/classifier/_logistic_regression.py
+++ b/skactiveml/classifier/_logistic_regression.py
@@ -15,12 +15,12 @@
 from sklearn.utils.validation import check_array, check_is_fitted, \
     column_or_1d
 
-from ..base import SkactivemlClassifier, AnnotModelMixing
+from ..base import SkactivemlClassifier, AnnotModelMixin
 from ..utils import MISSING_LABEL, compute_vote_vectors, \
     ext_confusion_matrix, rand_argmax
 
 
-class LogisticRegressionRY(SkactivemlClassifier, AnnotModelMixing):
+class LogisticRegressionRY(SkactivemlClassifier, AnnotModelMixin):
     """LogisticRegressionRY
 
     Logistic Regression based on Raykar [1] is a classification algorithm that
diff --git a/skactiveml/classifier/_wrapper.py b/skactiveml/classifier/_wrapper.py
index a84017b3..298205b1 100644
--- a/skactiveml/classifier/_wrapper.py
+++ b/skactiveml/classifier/_wrapper.py
@@ -177,7 +177,8 @@ def predict_proba(self, X, **predict_proba_kwargs):
             if len(self.estimator_.classes_) != len(self.classes_):
                 P_ext = np.zeros((len(X), len(self.classes_)))
                 class_indices = np.asarray(self.estimator_.classes_, dtype=int)
-                P_ext[:, class_indices] = P
+                # Exception for MLPCLassifier
+                P_ext[:, class_indices] = 1 if len(class_indices) == 1 else P
                 P = P_ext
             if not np.any(np.isnan(P)):
                 return P
@@ -215,8 +216,8 @@ def _fit(self, fit_function, X, y, sample_weight=None, **fit_kwargs):
         if not has_fit_parameter(self.estimator, 'sample_weight') \
                 and sample_weight is not None:
             warnings.warn(
-                "{} does not support 'sample_weight'. "
-                "Therefore, they will be ignored.".format(self.estimator))
+                f"{self.estimator} does not support `sample_weight`. "
+                f"Therefore, this parameter will be ignored.")
         self.estimator_ = deepcopy(self.estimator)
         # Transform in case of 2-dimensional array of class labels.
         if y.ndim == 2:
@@ -228,26 +229,28 @@ def _fit(self, fit_function, X, y, sample_weight=None, **fit_kwargs):
         self._label_counts = [np.sum(y[is_lbld] == c) for c in
                               range(len(self._le.classes_))]
         try:
+            X_lbld = X[is_lbld].astype(np.float32)
+            y_lbld = y[is_lbld].astype(np.int64)
             if not has_fit_parameter(self.estimator,
                                      'sample_weight') or sample_weight is None:
                 if fit_function == 'partial_fit':
                     classes = self._le.transform(self.classes_)
-                    self.estimator_.partial_fit(X=X[is_lbld], y=y[is_lbld],
+                    self.estimator_.partial_fit(X=X_lbld, y=y_lbld,
                                                 classes=classes,
                                                 **fit_kwargs)
                 elif fit_function == 'fit':
-                    self.estimator_.fit(X=X[is_lbld], y=y[is_lbld],
+                    self.estimator_.fit(X=X_lbld, y=y_lbld,
                                         **fit_kwargs)
             else:
                 if fit_function == 'partial_fit':
                     classes = self._le.transform(self.classes_)
-                    self.estimator_.partial_fit(X=X[is_lbld], y=y[is_lbld],
+                    self.estimator_.partial_fit(X=X_lbld, y=y_lbld,
                                                 classes=classes,
                                                 sample_weight=sample_weight[
                                                     is_lbld],
                                                 **fit_kwargs)
                 elif fit_function == 'fit':
-                    self.estimator_.fit(X=X[is_lbld], y=y[is_lbld],
+                    self.estimator_.fit(X=X_lbld, y=y_lbld,
                                         sample_weight=sample_weight[is_lbld],
                                         **fit_kwargs)
             self.is_fitted_ = True
diff --git a/skactiveml/pool/__init__.py b/skactiveml/pool/__init__.py
index 5934b3b0..520ea21c 100644
--- a/skactiveml/pool/__init__.py
+++ b/skactiveml/pool/__init__.py
@@ -1,17 +1,20 @@
 """
-The :mod:`skactiveml.pool` module implements query strategies for pool-based
-active learning.
+The :mod:`skactiveml.pool` package implements query strategies for
+pool-based active learning.
 """
 
-from ._probal import McPAL
-from ._random import RandomSampler
-from ._uncertainty import UncertaintySampling, expected_average_precision
+from ._alce import ALCE
 from ._epistemic_uncertainty import EpistemicUncertainty
-from ._qbc import QBC, average_kl_divergence, vote_entropy
-from ._expected_error import ExpectedErrorReduction
+from ._expected_error import ExpectedErrorReduction, expected_error_reduction
 from ._four_ds import FourDS
-from ._alce import ALCE
+from ._probal import McPAL, cost_reduction
+from ._qbc import QBC, average_kl_divergence, vote_entropy
+from ._random import RandomSampler
+from ._uncertainty import UncertaintySampling, uncertainty_scores, \
+    expected_average_precision
 
-__all__ = ['RandomSampler', 'McPAL', 'UncertaintySampling',
-           'EpistemicUncertainty', 'ExpectedErrorReduction', 'QBC', 'FourDS',
-           'ALCE']
+__all__ = ['RandomSampler', 'McPAL', 'cost_reduction', 'UncertaintySampling',
+           'uncertainty_scores', 'expected_average_precision',
+           'EpistemicUncertainty', 'ExpectedErrorReduction',
+           'expected_error_reduction', 'QBC', 'average_kl_divergence',
+           'vote_entropy', 'FourDS', 'ALCE']
diff --git a/skactiveml/pool/_epistemic_uncertainty.py b/skactiveml/pool/_epistemic_uncertainty.py
index 653a60f4..fb2526fa 100644
--- a/skactiveml/pool/_epistemic_uncertainty.py
+++ b/skactiveml/pool/_epistemic_uncertainty.py
@@ -1,20 +1,20 @@
 """
 Epistemic uncertainty query strategy
 """
-
 # Author: Pascal Mergard <Pascal.Mergard@student.uni-kassel.de>
+#         Marek Herde <marek.herde@uni-kassel.de>
 import warnings
 
 import numpy as np
 from scipy.interpolate import griddata
 from scipy.optimize import minimize_scalar, minimize, LinearConstraint
-from sklearn.base import clone
 from sklearn.linear_model import LogisticRegression
 from sklearn.linear_model._logistic import _logistic_loss
 
-from ..base import SingleAnnotPoolBasedQueryStrategy, SkactivemlClassifier
+from ..base import SingleAnnotPoolBasedQueryStrategy
 from ..classifier import SklearnClassifier, PWC
-from ..utils import is_labeled, check_X_y, simple_batch, check_scalar
+from ..utils import is_labeled, check_X_y, simple_batch, check_scalar, \
+    fit_if_not_fitted
 
 
 class EpistemicUncertainty(SingleAnnotPoolBasedQueryStrategy):
@@ -25,25 +25,12 @@ class EpistemicUncertainty(SingleAnnotPoolBasedQueryStrategy):
 
     Parameters
     ----------
-    clf : SkactivemlClassifier
-        A classifier. Only skactiveml.classifier.PWC or
-        sklearn.linear_model.LogisticRegression are supported.
     precompute : boolean (default=False)
         Whether the epistemic uncertainty should be precomputed.
         Only for PWC significant.
     random_state : numeric | np.random.RandomState
         The random state to use.
 
-    Attributes
-    ----------
-    clf : SkactivemlClassifier
-        The given SkactivemlClassifier.
-    precompute : boolean (default=False)
-        Whether the epistemic uncertainty should be precomputed.
-        Only for PWC significant.
-    random_state : numeric | np.random.RandomState
-        Random state to use.
-
     References
     ---------
     [1] Nguyen, Vu-Linh, Sébastien Destercke, and Eyke Hüllermeier.
@@ -51,67 +38,64 @@ class EpistemicUncertainty(SingleAnnotPoolBasedQueryStrategy):
         Discovery Science. Springer, Cham, 2019.
     """
 
-    def __init__(self, clf, precompute=False, random_state=None):
+    def __init__(self, precompute=False, random_state=None):
         super().__init__(random_state=random_state)
-
-        self.clf = clf
         self.precompute = precompute
 
-    def query(self, X_cand, X, y, sample_weight=None, batch_size=1,
+    def query(self, X_cand, clf, X, y, sample_weight=None, batch_size=1,
               return_utilities=False):
         """
         Queries the next instance to be labeled.
 
         Parameters
         ----------
-        X_cand : np.ndarray
-            The unlabeled pool from which to choose.
-        X : np.ndarray
-            The labeled pool used to fit the classifier.
-        y : np.array
-            The labels of the labeled pool X.
-        sample_weight : array-like of shape (n_samples,) (default=None)
-            Sample weights for X, only used if clf is a logistic regression
-            classifier.
+        X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+        clf : {SkactivemlClassifier, PWC}
+            Only the PWC and a wrapped logistic regression are supported as
+            classifiers.
+        X : array-like, shape (n_samples, n_features),
+            Complete training data set.
+        y : array-like, shape (n_samples),
+            Labels of the training data set.
+        sample_weight : array-like, shape (n_samples), optional (default=None)
+            Weights of training samples in `X`.
         batch_size : int, optional (default=1)
             The number of samples to be selected in one AL cycle.
-        return_utilities : bool (default=False)
-            If True, the utilities are returned.
+        return_utilities : bool, optional (default=False)
+            If true, also return the utilities based on the query strategy.
 
         Returns
         -------
-        best_indices : np.ndarray, shape (batch_size)
-            The index of the queried instance.
-        batch_utilities : np.ndarray,  shape (batch_size, len(X_cand))
-            The utilities of all instances of
-            X_cand(if return_utilities=True).
+        query_indices : numpy.ndarray, shape (batch_size)
+            The query_indices indicate for which candidate sample a label is
+            to queried, e.g., `query_indices[0]` indicates the first selected
+            sample.
+        utilities : numpy.ndarray, shape (batch_size, n_samples)
+            The utilities of all candidate samples after each selected
+            sample of the batch, e.g., `utilities[0]` indicates the utilities
+            used for selecting the first sample (with index `query_indices[0]`)
+            of the batch.
         """
         # Validate input parameters.
         X_cand, return_utilities, batch_size, random_state = \
             self._validate_data(X_cand, return_utilities, batch_size,
                                 self.random_state, reset=True)
-
+        if X is None or y is None:
+            raise ValueError('`X` and `y` cannot be None.')
         X, y, sample_weight = check_X_y(X, y, sample_weight=sample_weight)
-        # Check if the attribute clf is valid
-        if not isinstance(self.clf, SkactivemlClassifier):
-            raise TypeError(
-                'clf as to be from type SkactivemlClassifier. The #'
-                'given type is {}. Use the wrapper in '
-                'skactiveml.classifier to use a sklearn '
-                'classifier/ensemble.'.format(type(self.clf)))
-
-        # fit the classifier and get the probabilities
-        clf = clone(self.clf)
-        clf.fit(X, y, sample_weight=sample_weight)
-
-        # chose the correct method for the given clf
+
+        # Fit the classifier.
+        clf = fit_if_not_fitted(clf, X, y, sample_weight, print_warning=False)
+
+        # Chose the correct method for the given classifier.
         if isinstance(clf, PWC):
             if not hasattr(self, 'precompute_array'):
                 self._precompute_array = None
 
-            # create precompute_array if necessary
+            # Create precompute_array if necessary.
             if not isinstance(self.precompute, bool):
-                raise TypeError("'precompute' should be from type bool but {} "
+                raise TypeError("'precompute' should be of type bool but {} "
                                 "were given".format(type(self.precompute)))
             if self.precompute and self._precompute_array is None:
                 self._precompute_array = np.full((2, 2), np.nan)
@@ -120,17 +104,16 @@ def query(self, X_cand, X, y, sample_weight=None, batch_size=1,
             utilities, self._precompute_array = _epistemic_uncertainty_pwc(
                 freq, self._precompute_array)
         elif isinstance(clf, SklearnClassifier) and \
-                isinstance(clf.estimator, LogisticRegression):
+                isinstance(clf.estimator_, LogisticRegression):
             mask_labeled = is_labeled(y, clf.missing_label)
             utilities = _epistemic_uncertainty_logreg(
                 X_cand=X_cand, X=X[mask_labeled], y=y[mask_labeled], clf=clf,
                 sample_weight=sample_weight[mask_labeled]
             )
         else:
-            raise TypeError("'clf' must be from type PWC or "
-                            "a wrapped LogisticRegression classifier. "
-                            "The given is from type {}."
-                            "".format(type(self.clf)))
+            raise TypeError(f"`clf` must be of type `PWC` or "
+                            f"a wrapped `LogisticRegression` classifier. "
+                            f"The given is of type {type(clf)}.")
 
         return simple_batch(utilities, random_state,
                             batch_size=batch_size,
@@ -305,8 +288,8 @@ def _epistemic_uncertainty_logreg(X_cand, X, y, clf, sample_weight=None):
         The labeled pool used to fit the classifier.
     y : np.array
         The labels of the labeled pool X.
-    clf : LogisticRegression
-        The fitted classifier.
+    clf : skactiveml.classifier.SklearnClassifier
+        Only a wrapped logistic regression is supported as classifier.
     sample_weight : array-like of shape (n_samples,) (default=None)
         Sample weights for X, only used if clf is a logistic regression
         classifier.
@@ -324,8 +307,8 @@ def _epistemic_uncertainty_logreg(X_cand, X, y, clf, sample_weight=None):
     """
     if not isinstance(clf, SklearnClassifier) \
             or not isinstance(clf.estimator, LogisticRegression):
-        raise TypeError('clf has to be a wrapped LogisticRegression classifier '
-                        'but \n{}\n was given.'.format(clf))
+        raise TypeError('clf has to be a wrapped LogisticRegression '
+                        'classifier but \n{}\n was given.'.format(clf))
     if len(clf.classes) != 2:
         raise ValueError('epistemic is only implemented for two-class '
                          'problems, {} classes were given.'
diff --git a/skactiveml/pool/_expected_error.py b/skactiveml/pool/_expected_error.py
index 7f0f7256..a1a40e61 100644
--- a/skactiveml/pool/_expected_error.py
+++ b/skactiveml/pool/_expected_error.py
@@ -1,41 +1,48 @@
+from copy import deepcopy
+
 import numpy as np
 from sklearn.base import clone
 
-from skactiveml.base import SingleAnnotPoolBasedQueryStrategy, \
-    SkactivemlClassifier
-from skactiveml.utils import check_classifier_params, \
-    check_X_y, is_labeled, simple_batch
+from ..base import SingleAnnotPoolBasedQueryStrategy, SkactivemlClassifier
+from ..utils import check_type, is_labeled, simple_batch, \
+    fit_if_not_fitted, check_cost_matrix, check_X_y
 
 
 class ExpectedErrorReduction(SingleAnnotPoolBasedQueryStrategy):
-    """Expected Error Reduction.
+    """Expected Error Reduction
 
     This class implements the expected error reduction algorithm with different
     loss functions:
      - log loss (log_loss) [1],
      - expected misclassification risk (emr) [2],
-     - and cost-sensitive learning (csl) [2].
+     - and cost-sensitive learning (csl) [3].
 
     Parameters
     ----------
-    clf : SkactivemlClassifier
-        Model implementing the methods 'fit' and and 'predict_proba'.
     method: {'log_loss', 'emr', 'csl'}, optional (default='emr')
         Variant of expected error reduction to be used: 'log_loss' is
         cost-insensitive, while 'emr' and 'csl' are cost-sensitive variants.
-    cost_matrix: array-like, shape (n_classes, n_classes),
-                 optional (default=None)
-        Cost matrix with C[i,j] defining the cost of predicting class j for a
-        sample with the actual class i. Only supported for least confident
+    cost_matrix: array-like, shape (n_classes, n_classes), optional
+    (default=None)
+        Cost matrix with `cost_matrix[i,j]` defining the cost of predicting
+        class `j` for a sample with the actual class `i`.
+        Only supported for least confident
         variant.
-    random_state: numeric | np.random.RandomState, optional (defatult=None)
+    ignore_partial_fit: bool, optional (default=False)
+        If false, the classifier will be refitted through `partial_fit` if
+        available. Otherwise, the use of `fit` is enforced.
+    random_state: numeric | np.random.RandomState, optional (default=None)
         Random state for annotator selection.
 
     References
     ----------
     [1] Settles, Burr. "Active learning literature survey." University of
         Wisconsin, Madison 52.55-66 (2010): 11.
-    [2] Margineantu, D. D. (2005, July). Active cost-sensitive learning.
+    [2] Joshi, A. J., Porikli, F., & Papanikolopoulos, N. (2009, June).
+        Multi-class active learning for image classification.
+        In 2009 IEEE Conference on Computer Vision and Pattern Recognition
+        (pp. 2372-2379). IEEE.
+    [3] Margineantu, D. D. (2005, July). Active cost-sensitive learning.
         In IJCAI (Vol. 5, pp. 1622-1623).
     """
 
@@ -43,42 +50,49 @@ class ExpectedErrorReduction(SingleAnnotPoolBasedQueryStrategy):
     CSL = 'csl'
     LOG_LOSS = 'log_loss'
 
-    def __init__(self, clf, method=EMR, cost_matrix=None,
+    def __init__(self, method=EMR, cost_matrix=None, ignore_partial_fit=False,
                  random_state=None):
         super().__init__(random_state=random_state)
-        self.clf = clf
         self.method = method
         self.cost_matrix = cost_matrix
+        self.ignore_partial_fit = ignore_partial_fit
 
-    def query(self, X_cand, X, y, sample_weight=None, sample_weight_cand=None,
-              batch_size=1, return_utilities=False):
+    def query(self, X_cand, clf, X=None, y=None, sample_weight=None,
+              sample_weight_cand=None, batch_size=1, return_utilities=False):
         """Query the next instance to be labeled.
 
         Parameters
         ----------
-        X_cand: array-like, shape (n_candidates, n_features)
-            Unlabeled candidate samples
-        X: array-like, shape (n_samples, n_features)
-            Complete data set
-        y: array-like, shape (n_samples)
-            Labels of the data set
-        sample_weight: array-like, shape (n_samples), optional (default=None)
-            Weights for uncertain annotators
-        sample_weight_cand: array-like, shape (n_candidates),
-                            optional (default=None)
-            Weights for the candidate samples
-        batch_size: int, optional (default=1)
-            The number of instances to be selected.
-        return_utilities: bool, optional (default=False)
-            If True, the utilities are additionally returned.
+        X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+        clf : skactiveml.base.SkactivemlClassifier
+            Model implementing the methods `fit` and `predict_proba`.
+        X : array-like, shape (n_samples, n_features), optional (default=None)
+            Complete training data set.
+        y : array-like, shape (n_samples), optional (default=None)
+            Labels of the training data set.
+        sample_weight : array-like, shape (n_samples), optional
+        (default=None)
+            Weights of training samples in `X`.
+        sample_weight_cand : array-like, shape (n_candidate_samples), optional
+        (default=None)
+            Weights of candidate samples in `X_cand`.
+        batch_size : int, optional (default=1)
+            The number of samples to be selected in one AL cycle.
+        return_utilities : bool, optional (default=False)
+            If true, also return the utilities based on the query strategy.
 
         Returns
         -------
-        query_indices: np.ndarray, shape (batch_size)
-            The index of the queried instance.
-        utilities: np.ndarray, shape (batch_size, n_candidates)
-            The utilities of all instances in X_cand
-            (only returned if return_utilities is True).
+        query_indices : numpy.ndarray, shape (batch_size)
+            The query_indices indicate for which candidate sample a label is
+            to queried, e.g., `query_indices[0]` indicates the first selected
+            sample.
+        utilities : numpy.ndarray, shape (batch_size, n_samples)
+            The utilities of all candidate samples after each selected
+            sample of the batch, e.g., `utilities[0]` indicates the utilities
+            used for selecting the first sample (with index `query_indices[0]`)
+            of the batch.
         """
         # Validate input parameters.
         X_cand, return_utilities, batch_size, random_state = \
@@ -86,92 +100,159 @@ def query(self, X_cand, X, y, sample_weight=None, sample_weight_cand=None,
                                 self.random_state, reset=True)
 
         # Calculate utilities
-        utilities = _expected_error_reduction(self.clf, X_cand, X, y,
-                                              self.cost_matrix, self.method,
-                                              sample_weight,
-                                              sample_weight_cand)
+        utilities = expected_error_reduction(
+            clf=clf, X_cand=X_cand, X=X, y=y, cost_matrix=self.cost_matrix,
+            method=self.method, sample_weight=sample_weight,
+            sample_weight_cand=sample_weight_cand,
+            ignore_partial_fit=self.ignore_partial_fit
+        )
 
         return simple_batch(utilities, random_state,
                             batch_size=batch_size,
                             return_utilities=return_utilities)
 
 
-def _expected_error_reduction(clf, X_cand, X, y, C, method='emr',
-                              sample_weight_cand=None, sample_weight=None):
-    """Compute least confidence as uncertainty scores.
+def expected_error_reduction(clf, X_cand, X=None, y=None, cost_matrix=None,
+                             method='emr', sample_weight_cand=None,
+                             sample_weight=None, ignore_partial_fit=False):
+    """Compute uncertainty scores.
 
     In case of a given cost matrix C, maximum expected cost is implemented as
     score.
 
     Parameters
     ----------
-    clf: sklearn classifier with predict_proba method
-        Model whose expected error reduction is measured.
-    X_cand: array-like, shape (n_candidates, n_features)
-        Unlabeled candidate samples
-    X: array-like, shape (n_samples, n_features)
-        Complete data set
-    y: array-like, shape (n_samples)
-        Labels of the data set
-    C: array-like, shape (n_classes, n_classes)
-        Cost matrix with C[i,j] defining the cost of predicting class j for a
-        sample with the actual class i.
-        Only supported for least confident variant.
-    method: {'log_loss', 'emr', 'csl'}, optional (default='emr')
+    clf : skactiveml.base.SkactivemlClassifier
+            Model implementing the methods `fit` and `predict_proba`.
+    X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+    X : array-like, shape (n_samples, n_features), optional (default=None)
+        Complete training data set.
+    y : array-like, shape (n_samples), optional (default=None)
+        Labels of the training data set.
+    cost_matrix : array-like, shape (n_classes, n_classes), optional
+    (default=None)
+        Cost matrix with `cost_matrix[i,j]` defining the cost of predicting
+        class `j` for a sample with the actual class `i`.
+        Only supported for least confident
+        variant.
+    method : {'log_loss', 'emr', 'csl'}, optional (default='emr')
         Variant of expected error reduction to be used: 'log_loss' is
         cost-insensitive, while 'emr' and 'csl' are cost-sensitive variants.
-    sample_weight: array-like, shape (n_samples), optional (default=None)
-        Weights for uncertain annotators
-    sample_weight_cand: array-like, shape (n_candidates),
-                        optional (default=None)
-        Weights for the candidate samples
+    sample_weight : array-like, shape (n_samples), optional
+    (default=None)
+        Weights of training samples in `X`.
+    sample_weight_cand : array-like, shape (n_candidate_samples), optional
+    (default=None)
+        Weights of candidate samples in `X_cand`.
+    ignore_partial_fit : bool, optional (default=False)
+        If false, the classifier will be refitted through `partial_fit` if
+        available. Otherwise, the use of `fit` is enforced.
 
     Returns
     -------
-    utilities: np.ndarray, shape (n_unlabeled_samples)
+    utilities : np.ndarray, shape (n_candidates)
         The utilities of all unlabeled instances.
+
+    References
+    ----------
+    [1] Settles, Burr. "Active learning literature survey." University of
+        Wisconsin, Madison 52.55-66 (2010): 11.
+    [2] Joshi, A. J., Porikli, F., & Papanikolopoulos, N. (2009, June).
+        Multi-class active learning for image classification.
+        In 2009 IEEE Conference on Computer Vision and Pattern Recognition
+        (pp. 2372-2379). IEEE.
+    [3] Margineantu, D. D. (2005, July). Active cost-sensitive learning.
+        In IJCAI (Vol. 5, pp. 1622-1623).
     """
-    # Check if the classifier and its arguments are valid
-    if not isinstance(clf, SkactivemlClassifier):
-        raise TypeError("'clf' must implement methods according to "
-                        "'SkactivemlClassifier'.")
-    check_classifier_params(clf.classes, clf.missing_label, C)
+    # Check if the classifier and its arguments are valid.
+    check_type(clf, SkactivemlClassifier, 'clf')
 
-    # Check the given data
+    # Check whether to use `fit` or `partial_fit`.
+    check_type(ignore_partial_fit, bool, 'ignore_partial_fit')
+    use_fit = ignore_partial_fit or not hasattr(clf, 'partial_fit')
+    if use_fit and (X is None or y is None):
+        raise ValueError(
+            '`X` and `y` cannot be None for a classifier using `fit` for '
+            'retraining.'
+        )
+    if (X is None or y is None) and method == 'csl':
+        raise ValueError(
+            "`X` and `y` cannot be None for `method='csl'`."
+        )
+    use_sample_weight = sample_weight is not None \
+                        or sample_weight_cand is not None
+    if use_fit and (bool(sample_weight is None)
+                    != bool(sample_weight_cand is None)):
+        raise ValueError(
+            '`sample_weight` and `sample_weight_cand` must either both be '
+            'None or array-like, if the fit method is used.'
+        )
     X, y, X_cand, sample_weight, sample_weight_cand = check_X_y(
         X, y, X_cand, sample_weight, sample_weight_cand,
         force_all_finite=False, missing_label=clf.missing_label
     )
 
-    clf = clone(clf)
-    clf.fit(X, y, sample_weight)
+    # Refit classifier.
+    if use_sample_weight:
+        clf = fit_if_not_fitted(clf, X, y, sample_weight, False)
+    else:
+        clf = fit_if_not_fitted(clf, X, y, None, False)
+    clf_refit = clone(clf).fit if use_fit else deepcopy(clf).partial_fit
 
-    n_classes = len(clf.classes)
+    # Check cost matrix.
+    n_classes = len(clf.classes_)
+    cost_matrix = 1 - np.eye(len(clf.classes_)) if cost_matrix is None else \
+        cost_matrix
+    cost_matrix = check_cost_matrix(cost_matrix, n_classes)
+
+    # Compute class-membership probabilities of candidate samples.
     P = clf.predict_proba(X_cand)
-    C = 1 - np.eye(np.size(P, axis=1)) if C is None else C
+
+    # Storage for computed errors per candidate sample.
     errors = np.zeros(len(X_cand))
     errors_per_class = np.zeros(n_classes)
+
+    # Iterate over candidate samples
     for i, x in enumerate(X_cand):
-        w = sample_weight_cand[i]
+        # Simulate acquisition of label for each candidate sample and class.
         for yi in range(n_classes):
-            clf.fit(np.vstack((X, [x])),
-                    np.append(y, [[yi]]),
-                    np.append(sample_weight, w))
+            # Create sample array for the retraining of the classifier.
+            X_new = np.vstack((X, [x])) if use_fit else np.array([x])
+            # Create label array for the retraining of the classifier.
+            y_new = np.append(y, [[yi]]) if use_fit else np.array([yi])
+            # Check whether sample weights are used.
+            if use_sample_weight:
+                # Create sample weight array for the retraining of the
+                # classifier.
+                w = sample_weight_cand[i]
+                if use_fit:
+                    sample_weight_new = np.append(sample_weight, [[w]])
+                else:
+                    sample_weight_new = np.array([w])
+                # Retrain classifier with sample weights.
+                clf_new = clf_refit(X_new, y_new, sample_weight_new)
+            else:
+                # Retrain classifier without sample weights.
+                clf_new = clf_refit(X_new, y_new)
             if method == 'emr':
-                P_new = clf.predict_proba(X_cand)
-                costs = np.sum((P_new.T[:, None] * P_new.T).T * C)
+                P_new = clf_new.predict_proba(X_cand)
+                costs = np.sum((P_new.T[:, None] * P_new.T).T * cost_matrix)
             elif method == 'csl':
-                labeled_indices = is_labeled(y, clf.missing_label)
-                X_labeled = X[labeled_indices]
-                y_labeled = y[labeled_indices]
-                y_indices = [np.where(clf.classes == label)[0][0]
+                is_lbld = is_labeled(y, clf_new.missing_label)
+                X_labeled = X[is_lbld]
+                y_labeled = y[is_lbld]
+                y_indices = [np.where(clf_new.classes_ == label)[0][0]
                              for label in y_labeled]
                 if len(X_labeled) > 0:
-                    costs = np.sum(clf.predict_proba(X_labeled) * C[y_indices])
+                    costs = np.sum(
+                        clf_new.predict_proba(X_labeled) *
+                        cost_matrix[y_indices]
+                    )
                 else:
                     costs = 0
             elif method == 'log_loss':
-                P_new = clf.predict_proba(X_cand)
+                P_new = clf_new.predict_proba(X_cand)
                 costs = -np.sum(P_new * np.log(P_new + np.finfo(float).eps))
             else:
                 raise ValueError(
diff --git a/skactiveml/pool/_four_ds.py b/skactiveml/pool/_four_ds.py
index 74d55c8b..1c98ddcc 100644
--- a/skactiveml/pool/_four_ds.py
+++ b/skactiveml/pool/_four_ds.py
@@ -1,12 +1,15 @@
-import numpy as np
+"""
+Module implementing 4DS active learning strategy.
+"""
+# Author: Marek Herde <marek.herde@uni-kassel.de>
 
-from copy import deepcopy
 
-from sklearn.utils import check_array, check_scalar, column_or_1d
+import numpy as np
+from sklearn.utils.validation import check_array, check_scalar, column_or_1d
 
 from ..base import SingleAnnotPoolBasedQueryStrategy
-from ..utils import rand_argmax, is_labeled
 from ..classifier import CMM
+from ..utils import rand_argmax, is_labeled, fit_if_not_fitted, check_type
 
 
 class FourDS(SingleAnnotPoolBasedQueryStrategy):
@@ -16,8 +19,6 @@ class FourDS(SingleAnnotPoolBasedQueryStrategy):
 
     Parameters
     ----------
-    clf : skactiveml.classifier.CMM
-        GMM-based Classifier to be trained.
     lmbda : float between 0 and 1, optional
     (default=min((batch_size-1)*0.05, 0.5))
         For the selection of more than one sample within each query round, 4DS
@@ -29,26 +30,30 @@ class FourDS(SingleAnnotPoolBasedQueryStrategy):
     References
     ---------
     [1] Reitmaier, T., & Sick, B. (2013). Let us know your decision: Pool-based
-    active training of a generative classifier with the selection strategy 4DS.
-    Information Sciences, 230, 106-131.
+        active training of a generative classifier with the selection strategy
+        4DS. Information Sciences, 230, 106-131.
     """
-    def __init__(self, clf, lmbda=None, random_state=None):
+
+    def __init__(self, lmbda=None, random_state=None):
         super().__init__(random_state=random_state)
-        self.clf = clf
         self.lmbda = lmbda
 
-    def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
-              batch_size=1):
+    def query(self, X_cand, clf, X=None, y=None, sample_weight=None,
+              return_utilities=False, batch_size=1):
         """Ask the query strategy which sample in 'X_cand' to query.
 
         Parameters
         ----------
-        X_cand : array-like, shape (n_samples, n_features)
+        X_cand : array-like, shape (n_candidate_samples, n_features)
             Candidate samples from which the strategy can select.
-        X : array-like, shape (n_samples, n_features)
-            Input samples used to fit the classifier.
-        y : array-like, shape (n_samples)
-            Labels of the input samples 'X'. There may be missing labels.
+        clf : skactiveml.classifier.CMM
+            GMM-based classifier to be trained.
+        X: array-like, shape (n_samples, n_features), optional (default=None)
+            Complete training data set.
+        y: array-like, shape (n_samples), optional (default=None)
+            Labels of the training data set.
+        sample_weight: array-like, shape (n_samples), optional (default=None)
+            Weights of training samples in `X`.
         batch_size : int, optional (default=1)
             The number of samples to be selected in one AL cycle.
         return_utilities : bool, optional (default=False)
@@ -77,10 +82,7 @@ def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
         y = column_or_1d(y)
 
         # Check classifier type.
-        if not isinstance(self.clf, CMM):
-            raise TypeError(
-                "'clf' must be a 'CMM' but got {}".format(type(self.clf)))
-        cmm = deepcopy(self.clf)
+        check_type(clf, CMM, 'clf')
 
         # Storage for query indices.
         query_indices = np.full(batch_size, fill_value=-1, dtype=int)
@@ -93,12 +95,12 @@ def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
                      max_val=1)
 
         # Fit the classifier and get the probabilities.
-        cmm.fit(X, y, sample_weight=sample_weight)
-        P_cand = cmm.predict_proba(X_cand)
-        R_cand = cmm.mixture_model_.predict_proba(X_cand)
-        is_lbld = is_labeled(y, missing_label=cmm.missing_label)
+        clf = fit_if_not_fitted(clf, X, y, sample_weight)
+        P_cand = clf.predict_proba(X_cand)
+        R_cand = clf.mixture_model_.predict_proba(X_cand)
+        is_lbld = is_labeled(y, missing_label=clf.missing_label)
         if np.sum(is_lbld) >= 1:
-            R_lbld = cmm.mixture_model_.predict_proba(X[is_lbld])
+            R_lbld = clf.mixture_model_.predict_proba(X[is_lbld])
         else:
             R_lbld = np.array([0])
 
@@ -110,7 +112,7 @@ def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
                 np.max(distance_cand) - np.min(distance_cand) + 1.e-5)
 
         # Compute densities according to Eq. 10 in [1].
-        density_cand = cmm.mixture_model_.score_samples(X_cand)
+        density_cand = clf.mixture_model_.score_samples(X_cand)
         density_cand = (density_cand - np.min(density_cand) + 1.e-5) / (
                 np.max(density_cand) - np.min(density_cand) + 1.e-5)
 
@@ -118,14 +120,14 @@ def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
         R_lbld_sum = np.sum(R_lbld, axis=0, keepdims=True)
         R_sum = R_cand + R_lbld_sum
         R_mean = R_sum / (len(R_lbld) + 1)
-        distribution_cand = cmm.mixture_model_.weights_ - R_mean
+        distribution_cand = clf.mixture_model_.weights_ - R_mean
         distribution_cand = np.maximum(np.zeros_like(distribution_cand),
                                        distribution_cand)
         distribution_cand = 1 - np.sum(distribution_cand, axis=1)
 
         # Compute rho according to Eq. 15  in [1].
         diff = np.sum(
-            np.abs(cmm.mixture_model_.weights_ - np.mean(R_lbld, axis=0)))
+            np.abs(clf.mixture_model_.weights_ - np.mean(R_lbld, axis=0)))
         rho = min(1, diff)
 
         # Compute e_dwus according to Eq. 13  in [1].
@@ -160,7 +162,7 @@ def query(self, X_cand, X, y, sample_weight=None, return_utilities=False,
                 R_sum = R_cand + np.sum(R_cand[is_selected], axis=0,
                                         keepdims=True) + R_lbld_sum
                 R_mean = R_sum / (len(R_lbld) + len(query_indices) + 1)
-                distribution_cand = cmm.mixture_model_.weights_ - R_mean
+                distribution_cand = clf.mixture_model_.weights_ - R_mean
                 distribution_cand = np.maximum(
                     np.zeros_like(distribution_cand), distribution_cand)
                 distribution_cand = 1 - np.sum(distribution_cand, axis=1)
diff --git a/skactiveml/pool/_probal.py b/skactiveml/pool/_probal.py
index b0e4eb61..71d8b8a3 100644
--- a/skactiveml/pool/_probal.py
+++ b/skactiveml/pool/_probal.py
@@ -1,14 +1,12 @@
 import itertools
-import numpy as np
 
+import numpy as np
 from scipy.special import factorial, gammaln
-from sklearn import clone
-from sklearn.utils import check_array
+from sklearn.utils.validation import check_array
 
-from skactiveml.base import SingleAnnotPoolBasedQueryStrategy
-from skactiveml.base import ClassFrequencyEstimator
-from skactiveml.utils import check_scalar, check_classifier_params, \
-    simple_batch
+from ..base import ClassFrequencyEstimator
+from ..base import SingleAnnotPoolBasedQueryStrategy
+from ..utils import check_scalar, simple_batch, check_type, fit_if_not_fitted
 
 
 class McPAL(SingleAnnotPoolBasedQueryStrategy):
@@ -19,8 +17,6 @@ class McPAL(SingleAnnotPoolBasedQueryStrategy):
 
     Parameters
     ----------
-    clf: BaseEstimator
-        Probabilistic classifier for gain calculation.
     prior: float, optional (default=1)
         Prior probabilities for the Dirichlet distribution of the samples.
     m_max: int, optional (default=1)
@@ -31,59 +27,67 @@ class McPAL(SingleAnnotPoolBasedQueryStrategy):
     References
     ----------
     [1] Daniel Kottke, Georg Krempl, Dominik Lang, Johannes Teschner, and Myra
-    Spiliopoulou.
-        Multi-Class Probabilistic Active Learning,
+        Spiliopoulou. Multi-Class Probabilistic Active Learning,
         vol. 285 of Frontiers in Artificial Intelligence and Applications,
         pages 586-594. IOS Press, 2016
     """
 
-    def __init__(self, clf, prior=1, m_max=1, random_state=None):
+    def __init__(self, prior=1, m_max=1, random_state=None):
         super().__init__(random_state=random_state)
-        self.clf = clf
         self.prior = prior
         self.m_max = m_max
 
-    def query(self, X_cand, X, y, sample_weight=None, utility_weight=None,
-              batch_size=1, return_utilities=False):
+    def query(self, X_cand, clf, X=None, y=None, sample_weight=None,
+              utility_weight=None, batch_size=1, return_utilities=False):
         """Query the next instance to be labeled.
 
         Parameters
         ----------
-        X_cand: array-like, shape(n_candidates, n_features)
-            Unlabeled candidate samples
-        X: array-like (n_training_samples, n_features)
-            Complete data set
-        y: array-like (n_training_samples)
-            Labels of the data set
-        sample_weight: array-like, shape (n_training_samples),
-                       optional (default=None)
-            Weights for uncertain annotators
-        batch_size: int, optional (default=1)
-            The number of instances to be selected.
-        utility_weight: array-like (n_candidate_samples)
-            Densities for each instance in X
-        return_utilities: bool (default=False)
-            If True, the utilities are additionally returned.
+        X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+        clf : skactiveml.base.ClassFrequencyEstimator
+            Model implementing the methods `fit` and `predict_proba`.
+        X: array-like, shape (n_samples, n_features), optional (default=None)
+            Complete training data set.
+        y: array-like, shape (n_samples), optional (default=None)
+            Labels of the training data set.
+        sample_weight: array-like, shape (n_samples), optional (default=None)
+            Weights of training samples in `X`.
+        utility_weight: array-like (n_candidate_samples), optional
+        (default=None)
+            Densities for each sample in `X`.
+        batch_size : int, optional (default=1)
+            The number of samples to be selected in one AL cycle.
+        return_utilities : bool, optional (default=False)
+            If true, also return the utilities based on the query strategy.
 
         Returns
         -------
-        query_indices: np.ndarray, shape (1)
-            The index of the queried instance.
-        utilities: np.ndarray, shape (1, n_candidates)
-            The utilities of all instances in X_cand
-            (only returned if return_utilities is True).
+        query_indices : numpy.ndarray, shape (batch_size)
+            The query_indices indicate for which candidate sample a label is
+            to queried, e.g., `query_indices[0]` indicates the first selected
+            sample.
+        utilities : numpy.ndarray, shape (batch_size, n_samples)
+            The utilities of all candidate samples after each selected
+            sample of the batch, e.g., `utilities[0]` indicates the utilities
+            used for selecting the first sample (with index `query_indices[0]`)
+            of the batch.
         """
-        # Validate input
+        # Validate input parameters.
         X_cand, return_utilities, batch_size, utility_weight, random_state = \
             self._validate_data(X_cand, return_utilities, batch_size,
                                 utility_weight, self.random_state, reset=True)
 
-        # Calculate utilities and return the output
-        clf = clone(self.clf)
-        clf.fit(X, y, sample_weight)
+        # Check the classifier's type.
+        check_type(clf, ClassFrequencyEstimator, 'clf')
+
+        # Fit the classifier and predict frequencies.
+        clf = fit_if_not_fitted(clf, X, y, sample_weight)
         k_vec = clf.predict_freq(X_cand)
-        utilities = utility_weight * _cost_reduction(k_vec, prior=self.prior,
-                                                     m_max=self.m_max)
+
+        # Calculate utilities and return the output.
+        utilities = utility_weight * cost_reduction(k_vec, prior=self.prior,
+                                                    m_max=self.m_max)
 
         return simple_batch(utilities, random_state,
                             batch_size=batch_size,
@@ -94,18 +98,13 @@ def _validate_data(self, X_cand, return_utilities, batch_size,
         X_cand, return_utilities, batch_size, random_state = \
             super()._validate_data(X_cand, return_utilities, batch_size,
                                    self.random_state, reset=True)
-        # Check if the classifier and its arguments are valid
-        if not isinstance(self.clf, ClassFrequencyEstimator):
-            raise TypeError("'clf' must implement methods according to "
-                            "'ClassFrequencyEstimator'.")
-        check_classifier_params(self.clf.classes, self.clf.missing_label)
 
-        # Check 'utility_weight'
+        # Check `utility_weight`.
         if utility_weight is None:
             utility_weight = np.ones(len(X_cand))
         utility_weight = check_array(utility_weight, ensure_2d=False)
 
-        # Check if X_cand and utility_weight have the same length
+        # Check if `X_cand` and `utility_weight` have the same length.
         if not len(X_cand) == len(utility_weight):
             raise ValueError(
                 "'X_cand' and 'utility_weight' must have the same length."
@@ -115,7 +114,7 @@ def _validate_data(self, X_cand, return_utilities, batch_size,
             random_state
 
 
-def _cost_reduction(k_vec_list, C=None, m_max=2, prior=1.e-3):
+def cost_reduction(k_vec_list, C=None, m_max=2, prior=1.e-3):
     """Calculate the expected cost reduction.
 
     Calculate the expected cost reduction for given maximum number of
@@ -172,10 +171,10 @@ def _cost_reduction(k_vec_list, C=None, m_max=2, prior=1.e-3):
                         for elem in list(itertools.product(*combs))])
 
     # three factors of the closed form solution
-    factor_1 = 1 / euler_beta(k_vec_list)
-    factor_2 = multinomial(l_vec_list)
-    factor_3 = euler_beta(np.sum(combs, axis=1)).reshape(n_samples, n_l_vecs,
-                                                         n_classes)
+    factor_1 = 1 / _euler_beta(k_vec_list)
+    factor_2 = _multinomial(l_vec_list)
+    factor_3 = _euler_beta(np.sum(combs, axis=1)).reshape(n_samples, n_l_vecs,
+                                                          n_classes)
 
     # expected classification cost for each m
     m_sums = np.asarray(
@@ -233,7 +232,7 @@ def _gen_l_vec_list(m_approx, n_classes):
     return label_vec_list
 
 
-def euler_beta(a):
+def _euler_beta(a):
     """
     Represents Euler beta function:
     B(a(i)) = Gamma(a(i,1))*...*Gamma(a_n)/Gamma(a(i,1)+...+a(i,n))
@@ -251,7 +250,7 @@ def euler_beta(a):
     return np.exp(np.sum(gammaln(a), axis=1) - gammaln(np.sum(a, axis=1)))
 
 
-def multinomial(a):
+def _multinomial(a):
     """
     Computes Multinomial coefficient:
     Mult(a(i)) = (a(i,1)+...+a(i,n))!/(a(i,1)!...a(i,n)!)
diff --git a/skactiveml/pool/_qbc.py b/skactiveml/pool/_qbc.py
index a3faed1e..e5f925c4 100644
--- a/skactiveml/pool/_qbc.py
+++ b/skactiveml/pool/_qbc.py
@@ -1,22 +1,17 @@
 """
-query by committee strategies
+Query-by-committee strategies.
 """
-
 # Author: Pascal Mergard <Pascal.Mergard@student.uni-kassel.de>
+#         Marek Herde <marek.herde@uni-kassel.de>
 
-import numpy as np
-import warnings
-import inspect
+from copy import deepcopy
 
-from sklearn import clone
+import numpy as np
+from sklearn.utils.validation import check_array, _is_arraylike
 
 from ..base import SingleAnnotPoolBasedQueryStrategy, SkactivemlClassifier
-
-from sklearn.ensemble import BaggingClassifier, BaseEnsemble
-from sklearn.utils import check_array
-
-from ..classifier import SklearnClassifier
-from ..utils import simple_batch, check_classes, ExtLabelEncoder
+from ..utils import simple_batch, fit_if_not_fitted, check_type, \
+    compute_vote_vectors
 
 
 class QBC(SingleAnnotPoolBasedQueryStrategy):
@@ -28,33 +23,11 @@ class QBC(SingleAnnotPoolBasedQueryStrategy):
 
     Parameters
     ----------
-    clf : SkactivemlClassifier
-        If clf is an wrapped ensemble, it will used as committee. If clf is a
-        classifier, it will used for ensemble construction with the specified
-        ensemble or with BaggigngClassifier, if ensemble is None. clf must
-        implementing the methods 'fit', 'predict'(for vote entropy) and
-        'predict_proba'(for KL divergence).
-    ensemble : BaseEnsemble, default=None
-        sklearn.ensemble used to construct the committee. If None,
-        baggingClassifier is used.
-    method : string, default='KL_divergence'
+    method : {'KL_divergence', 'vote_entropy'}, optional
+    (default='KL_divergence')
         The method to calculate the disagreement.
-        'vote_entropy' or 'KL_divergence' are possible.
-    random_state : numeric | np.random.RandomState
-        Random state to use.
-    ensemble_dict : dictionary
-        Will be passed on to the ensemble.
-
-    Attributes
-    ----------
-    ensemble : sklearn.ensemble
-        Ensemble used as committee. Implementing the methods 'fit',
-        'predict'(for vote entropy) and 'predict_proba'(for KL divergence).
-    method : string, default='KL_divergence'
-        The method to calculate the disagreement. 'vote_entropy' or
-        'KL_divergence' are possible.
-    random_state : numeric | np.random.RandomState
-        Random state to use.
+    random_state: numeric | np.random.RandomState, optional (default=None)
+        Random state for annotator selection.
 
     References
     ----------
@@ -66,123 +39,99 @@ class QBC(SingleAnnotPoolBasedQueryStrategy):
         Learning (ICML), pages 1-9. Morgan Kaufmann, 1998.
     """
 
-    def __init__(self, clf, ensemble=None, method='KL_divergence',
-                 random_state=None, ensemble_dict=None):
-
+    def __init__(self, method='KL_divergence', random_state=None):
         super().__init__(random_state=random_state)
-
         self.method = method
-        self.ensemble = ensemble
-        self.clf = clf
-        self.ensemble_dict = ensemble_dict
 
-
-    def query(self, X_cand, X, y, sample_weight=None,  batch_size=1,
-              return_utilities=False):
-        """
-        Queries the next instance to be labeled.
+    def query(self, X_cand, ensemble, X=None, y=None, sample_weight=None,
+              batch_size=1, return_utilities=False):
+        """Queries the next instance to be labeled.
 
         Parameters
         ----------
-        X_cand : array-like
-            The unlabeled pool from which to choose.
-        X : array-like
-            The labeled pool used to fit the classifier.
-        y : array-like
-            The labels of the labeled pool X.
-        sample_weight : array-like of shape (n_samples,) (default=None)
-            Sample weights.
+        X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+        ensemble : {skactiveml.base.SkactivemlClassifier, array-like}
+            If `ensemble` is a `SkactivemlClassifier`, it must have
+            `n_estimators` and `estimators_` after fitting as attribute. Then,
+            its estimators will be used as committee. If `ensemble` is
+            array-like, each element of this list must be
+            `SkactivemlClassifier` and will be used as committee member.
+        X: array-like, shape (n_samples, n_features), optional (default=None)
+            Complete training data set.
+        y: array-like, shape (n_samples), optional (default=None)
+            Labels of the training data set.
+        sample_weight: array-like, shape (n_samples), optional
+        (default=None)
+            Weights of training samples in `X`.
         batch_size : int, optional (default=1)
             The number of samples to be selected in one AL cycle.
-        return_utilities : bool (default=False)
-            If True, the utilities are returned.
+        return_utilities : bool, optional (default=False)
+            If true, also return the utilities based on the query strategy.
 
         Returns
         -------
-        best_indices : np.ndarray, shape (batch_size)
-            The index of the queried instance.
-        batch_utilities : np.ndarray,  shape (batch_size, len(X_cnad))
-            The utilities of all instances of
-            X_cand(if return_utilities=True).
+        query_indices : numpy.ndarray, shape (batch_size)
+            The query_indices indicate for which candidate sample a label is
+            to queried, e.g., `query_indices[0]` indicates the first selected
+            sample.
+        utilities : numpy.ndarray, shape (batch_size, n_samples)
+            The utilities of all candidate samples after each selected
+            sample of the batch, e.g., `utilities[0]` indicates the utilities
+            used for selecting the first sample (with index `query_indices[0]`)
+            of the batch.
         """
-        self._clf = clone(self.clf)
-
-        if self.ensemble_dict is None:
-            ensemble_dict = dict()
-        else:
-            ensemble_dict = self.ensemble_dict
-
         # Validate input parameters.
         X_cand, return_utilities, batch_size, random_state = \
             self._validate_data(X_cand, return_utilities, batch_size,
                                 self.random_state, reset=True)
 
-        # Check if the attribute clf is valid
-        if not isinstance(self._clf, SkactivemlClassifier):
-            raise TypeError('clf has to be from type SkactivemlClassifier. The '
-                            'given type is {}. Use the wrapper in '
-                            'skactiveml.classifier to use a sklearn '
-                            'classifier/ensemble.'.format(type(self.clf)))
-
-        # Extract classes from clf
-        label_encoder = ExtLabelEncoder(missing_label=self._clf.missing_label,
-                                        classes=self.clf.classes).fit(y)
-        classes = label_encoder.classes_
-
-        # Check self.clf and self.method
-        if not isinstance(self.method, str):
-            raise TypeError("{} is an invalid type for the attribute "
-                            "'self.method'.".format(type(self.method)))
+        # Check attributed `method`.
         if self.method not in ['KL_divergence', 'vote_entropy']:
             raise ValueError(
-                "The given method {} is not valid. Supported methods are "
-                "'KL_divergence' and 'vote_entropy'".format(self.method))
-
-        if self.method == 'KL_divergence' and \
-                getattr(self._clf, 'predict_proba', None) is None:
-            raise TypeError(
-                "'clf' must implement the method 'predict_proba'")
-
-        # Build ensemble if necessary.
-        if not isinstance(self._clf, SklearnClassifier) or \
-                not isinstance(self._clf.estimator, BaseEnsemble):
-            # Validate 'ensemble'.
-            if self.ensemble is None:
-                warnings.warn('\'ensemble\' is not specified, '
-                              '\'BaggingClassifier\' will be used.')
-                ensemble = BaggingClassifier
-            elif not callable(self.ensemble):
-                raise TypeError("{} is not valid for the attribute "
-                                "'self.ensemble'.".format(self.ensemble))
-            elif not isinstance(self.ensemble(), BaseEnsemble):
-                raise TypeError("{} is an invalid type for the attribute "
-                                "'self.ensemble'.".format(type(self.ensemble)))
+                f"The given method {self.method} is not valid. "
+                f"Supported methods are 'KL_divergence' and 'vote_entropy'")
+
+        # Check if the parameter `ensemble` is valid.
+        if isinstance(ensemble, SkactivemlClassifier) and \
+                hasattr(ensemble, 'n_estimators'):
+            ensemble = fit_if_not_fitted(
+                ensemble, X, y, sample_weight=sample_weight
+            )
+            classes = ensemble.classes_
+            if hasattr(ensemble, 'estimators_'):
+                est_arr = ensemble.estimators_
             else:
-                ensemble = self.ensemble
-            # Build ensemble.
-            parameters = inspect.signature(ensemble.__init__).parameters
-            if not isinstance(ensemble_dict, dict):
-                raise TypeError("ensemble_dict is not a dictionary.")
-            if 'base_estimator' in parameters:
-                ensemble_dict['base_estimator'] = self._clf
-            self._clf = SklearnClassifier(
-                ensemble(**ensemble_dict, random_state=random_state),
-                classes=classes
+                est_arr = [ensemble] * ensemble.n_estimators
+        elif _is_arraylike(ensemble):
+            est_arr = deepcopy(ensemble)
+            for i in range(len(est_arr)):
+                check_type(est_arr[i], SkactivemlClassifier, f'ensemble[{i}]')
+                est_arr[i] = fit_if_not_fitted(
+                    est_arr[i], X, y, sample_weight=sample_weight
+                )
+                if i > 0:
+                    np.testing.assert_array_equal(
+                        est_arr[i - 1].classes_, est_arr[i].classes_,
+                        err_msg=f'The inferred classes of the {i - 1}-th and '
+                                f'{i}-th are not equal. Set the `classes` '
+                                f'parameter of each ensemble member to avoid '
+                                f'this error.'
+                    )
+            classes = est_arr[0].classes_
+        else:
+            raise TypeError(
+                f'`ensemble` must either be a `{SkactivemlClassifier} '
+                f'with the attribute `n_esembles` and `estimators_` after '
+                f'fitting or a list of {SkactivemlClassifier} objects.'
             )
 
-        # fit the classifier
-        self._clf.fit(X, y, sample_weight=sample_weight)
-
-        # choose the disagreement method and calculate the utilities
-        if hasattr(self._clf, 'estimators_') and self._clf.is_fitted_:
-            est_arr = self._clf.estimators_
-        else:
-            est_arr = [self._clf] * self._clf.n_estimators
+        # Compute utilities.
         if self.method == 'KL_divergence':
-            probas = [est.predict_proba(X_cand) for est in est_arr]
+            probas = np.array([est.predict_proba(X_cand) for est in est_arr])
             utilities = average_kl_divergence(probas)
         elif self.method == 'vote_entropy':
-            votes = [est.predict(X_cand) for est in est_arr]
+            votes = np.array([est.predict(X_cand) for est in est_arr]).T
             utilities = vote_entropy(votes, classes)
 
         return simple_batch(utilities, random_state,
@@ -191,40 +140,33 @@ def query(self, X_cand, X, y, sample_weight=None,  batch_size=1,
 
 
 def average_kl_divergence(probas):
-    """
-    Calculate the average Kullback-Leibler (KL) divergence for measuring the
-    level of disagreement in QBC.
+    """Calculates the average Kullback-Leibler (KL) divergence for measuring
+    the level of disagreement in QBC.
 
     Parameters
     ----------
-    probas : array-like, shape (n_estimators, n_X_cand, n_classes)
-        The probability estimations of all estimators, instances and classes.
+    probas : array-like, shape (n_estimators, n_samples, n_classes)
+        The probability estimates of all estimators, samples, and classes.
 
     Returns
     -------
-    scores: np.ndarray, shape (n_X_cand)
+    scores: np.ndarray, shape (n_samples)
         The Kullback-Leibler (KL) divergences.
 
     References
     ----------
     [1] A. McCallum and K. Nigam. Employing EM in pool-based active learning
-    for text classification. In Proceedings of the International Conference on
-    Machine Learning (ICML), pages 359-367. Morgan Kaufmann, 1998.
+        for text classification. In Proceedings of the International Conference
+        on Machine Learning (ICML), pages 359-367. Morgan Kaufmann, 1998.
     """
-
-    # validation:
-    # check P
-    probas = check_array(probas, accept_sparse=False,
-                         accept_large_sparse=True, dtype="numeric", order=None,
-                         copy=False, force_all_finite=True, ensure_2d=True,
-                         allow_nd=True, ensure_min_samples=1,
-                         ensure_min_features=1, estimator=None)
+    # Check probabilities.
+    probas = check_array(probas, allow_nd=True)
     if probas.ndim != 3:
-        raise ValueError("Expected 2D array, got 1D array instead:"
-                         "\narray={}.".format(probas))
+        raise ValueError(
+            f"Expected 3D array, got {probas.ndim}D array instead."
+        )
 
-    # calculate the average KL divergence:
-    probas = np.array(probas)
+    # Calculate the average KL divergence.
     probas_mean = np.mean(probas, axis=0)
     with np.errstate(divide='ignore', invalid='ignore'):
         scores = np.nansum(
@@ -235,52 +177,36 @@ def average_kl_divergence(probas):
 
 
 def vote_entropy(votes, classes):
-    """
-    Calculate the vote entropy for measuring the level of disagreement in QBC.
+    """Calculates the vote entropy for measuring the level of disagreement in
+    QBC.
 
     Parameters
     ----------
-    votes : array-like, shape (n_estimators, n_X_cand)
-        The class predicted by the estimators for each instance.
+    votes : array-like, shape (n_samples, n_estimators)
+        The class predicted by the estimators for each sample.
     classes : array-like, shape (n_classes)
         A list of all possible classes.
 
     Returns
     -------
-    vote_entropy : np.ndarray, shape (n_X_cand))
-        The vote entropy of each instance in X_cand.
+    vote_entropy : np.ndarray, shape (n_samples)
+        The vote entropy of each row in `votes`.
 
     References
     ----------
     [1] Engelson, Sean P., and Ido Dagan.
-    "Minimizing manual annotation cost in supervised training from corpora."
-    arXiv preprint cmp-lg/9606030 (1996).
+        Minimizing manual annotation cost in supervised training from corpora.
+        arXiv preprint cmp-lg/9606030 (1996).
     """
-    # check votes to be valid
-    votes = check_array(votes, accept_sparse=False,
-                        accept_large_sparse=True, dtype=None, order=None,
-                        copy=False, force_all_finite=True, ensure_2d=True,
-                        allow_nd=False, ensure_min_samples=1,
-                        ensure_min_features=1, estimator=None)
+    # Check `votes` array.
+    votes = check_array(votes)
 
-    # Check classes to be valid
-    check_classes(classes)
-    try:
-        for c in np.unique(votes.flatten()):
-            if c not in classes:
-                raise ValueError("The votes element '{}' "
-                                 "is not in classes.".format(c))
-    except TypeError:
-        raise TypeError("The type of classes and votes are not compatible.")
-    # count the votes
-    vote_count = np.zeros((votes.shape[1], len(classes)))
-    for i in range(votes.shape[1]):
-        for c_idx, c in enumerate(classes):
-            for m in range(len(votes)):
-                vote_count[i, c_idx] += (votes[m, i] == c)
+    # Count the votes.
+    vote_count = compute_vote_vectors(y=votes, classes=classes,
+                                      missing_label=None)
 
-    # compute vote entropy
+    # Compute vote entropy.
     v = vote_count / len(votes)
     with np.errstate(divide='ignore', invalid='ignore'):
-        scores = -np.nansum(v*np.log(v), axis=1) / np.log(len(votes))
+        scores = -np.nansum(v * np.log(v), axis=1) / np.log(len(votes))
     return scores
diff --git a/skactiveml/pool/_uncertainty.py b/skactiveml/pool/_uncertainty.py
index 94d15751..7e1ea438 100644
--- a/skactiveml/pool/_uncertainty.py
+++ b/skactiveml/pool/_uncertainty.py
@@ -1,44 +1,41 @@
 """
-Uncertainty query strategies
+Module implementing various uncertainty based query strategies.
 """
 
-# Author: Pascal Mergard <Pascal.Mergard@student.uni-kassel.de>
+# Authors: Pascal Mergard <Pascal.Mergard@student.uni-kassel.de>
+#          Marek Herde <marek.herde@uni-kassel.de>
 
 import numpy as np
+from sklearn.utils.validation import check_array
 
-from sklearn.base import clone
-from sklearn.utils import check_array
-
-from ..base import SingleAnnotPoolBasedQueryStrategy,\
-    SkactivemlClassifier
+from ..base import SingleAnnotPoolBasedQueryStrategy, SkactivemlClassifier
 from ..utils import check_cost_matrix, simple_batch, check_classes, \
-    ExtLabelEncoder
+    fit_if_not_fitted, check_type
 
 
 class UncertaintySampling(SingleAnnotPoolBasedQueryStrategy):
-    """
-    Uncertainty Sampling query strategy.
+    """Uncertainty Sampling
+
+    This class implement various uncertainty based query strategies, i.e., the
+    standard uncertainty measures [1], cost-sensitive ones [2], and one
+    optimizing expected average precision [3].
 
     Parameters
     ----------
-    clf : sklearn classifier
-        A probabilistic sklearn classifier.
-    method : string (default='margin_sampling')
+    method : string (default='least_confident')
         The method to calculate the uncertainty, entropy, least_confident,
         margin_sampling, and expected_average_precision  are possible.
         Epistemic only works with Parzen Window Classifier or
         Logistic Regression.
     cost_matrix : array-like, shape (n_classes, n_classes)
-        Cost matrix with cost_matrix[i,j] defining the cost of predicting class j for a
-        sample with the actual class i. Only supported for least confident
-        variant.
+        Cost matrix with cost_matrix[i,j] defining the cost of predicting class
+        j for a sample with the actual class i. Only supported for
+        `least_confident` and `margin_sampling` variant.
     random_state : numeric | np.random.RandomState
         The random state to use.
 
     Attributes
     ----------
-    clf : sklearn classifier
-        A probabilistic sklearn classifier.
     method : string
         The method to calculate the uncertainty. Only entropy, least_confident,
         margin_sampling and expected_average_precisionare.
@@ -53,87 +50,86 @@ class UncertaintySampling(SingleAnnotPoolBasedQueryStrategy):
     ---------
     [1] Settles, Burr. Active learning literature survey.
         University of Wisconsin-Madison Department of Computer Sciences, 2009.
-        http://www.burrsettles.com/pub/settles.activelearning.pdf
-    [2] Wang, Hanmo, et al. "Uncertainty sampling for action recognition
+    [2] Chen, Po-Lung, and Hsuan-Tien Lin. "Active learning for multiclass
+        cost-sensitive classification using probabilistic models." 2013
+        Conference on Technologies and Applications of Artificial Intelligence.
+        IEEE, 2013.
+    [3] Wang, Hanmo, et al. "Uncertainty sampling for action recognition
         via maximizing expected average precision."
         IJCAI International Joint Conference on Artificial Intelligence. 2018.
     """
 
-    def __init__(self, clf, method='margin_sampling', cost_matrix=None,
+    def __init__(self, method='least_confident', cost_matrix=None,
                  random_state=None):
         super().__init__(random_state=random_state)
-
         self.method = method
         self.cost_matrix = cost_matrix
-        self.clf = clf
 
-    def query(self, X_cand, X, y, sample_weight=None, batch_size=1,
+    def query(self, X_cand, clf, X=None, y=None, sample_weight=None,
+              batch_size=1,
               return_utilities=False):
         """
         Queries the next instance to be labeled.
 
         Parameters
         ----------
-        X_cand : np.ndarray
-            The unlabeled pool from which to choose.
-        X : np.ndarray
-            The labeled pool used to fit the classifier.
-        y : np.array
-            The labels of the labeled pool X.
-        sample_weight : array-like of shape (n_samples,) (default=None)
-            Sample weights for X, used to fit the clf.
+        X_cand : array-like, shape (n_candidate_samples, n_features)
+            Candidate samples from which the strategy can select.
+        clf : skactiveml.base.SkactivemlClassifier
+            Model implementing the methods `fit` and `predict_proba`.
+        X: array-like, shape (n_samples, n_features), optional (default=None)
+            Complete training data set.
+        y: array-like, shape (n_samples), optional (default=None)
+            Labels of the training data set.
+        sample_weight: array-like, shape (n_samples), optional
+        (default=None)
+            Weights of training samples in `X`.
         batch_size : int, optional (default=1)
             The number of samples to be selected in one AL cycle.
-        return_utilities : bool (default=False)
-            If True, the utilities are returned.
+        return_utilities : bool, optional (default=False)
+            If true, also return the utilities based on the query strategy.
 
         Returns
         -------
-        best_indices : np.ndarray, shape (batch_size)
-            The index of the queried instance.
-        batch_utilities : np.ndarray,  shape (batch_size, len(X_cand))
-            The utilities of all instances of
-            X_cand(if return_utilities=True).
+        query_indices : numpy.ndarray, shape (batch_size)
+            The query_indices indicate for which candidate sample a label is
+            to queried, e.g., `query_indices[0]` indicates the first selected
+            sample.
+        utilities : numpy.ndarray, shape (batch_size, n_samples)
+            The utilities of all candidate samples after each selected
+            sample of the batch, e.g., `utilities[0]` indicates the utilities
+            used for selecting the first sample (with index `query_indices[0]`)
+            of the batch.
         """
-        self._clf = clone(self.clf)
-
         # Validate input parameters.
         X_cand, return_utilities, batch_size, random_state = \
             self._validate_data(X_cand, return_utilities, batch_size,
                                 self.random_state, reset=True)
 
-        # Check if the attribute clf is valid
-        if not isinstance(self._clf, SkactivemlClassifier):
-            raise TypeError('clf has to be from type SkactivemlClassifier. '
-                            'The given type is {}. Use the wrapper in '
-                            'skactiveml.classifier to use a sklearn '
-                            'classifier/ensemble.'.format(type(self._clf)))
-
-        # Extract classes from clf
-        label_encoder = ExtLabelEncoder(missing_label=self._clf.missing_label,
-                                        classes=self.clf.classes).fit(y)
-        classes = label_encoder.classes_
+        # Validate classifier type.
+        check_type(clf, SkactivemlClassifier, 'clf')
 
-        # check self.method
+        # Validate method.
         if not isinstance(self.method, str):
             raise TypeError('{} is an invalid type for method. Type {} is '
                             'expected'.format(type(self.method), str))
 
-        if getattr(self.clf, 'predict_proba', None) is None:
-            raise TypeError("'clf' must implement the method 'predict_proba'")
+        # Fit the classifier.
+        clf = fit_if_not_fitted(clf, X, y, sample_weight)
 
-        # fit the classifier and get the probabilities
-        self._clf.fit(X, y, sample_weight=sample_weight)
-        probas = self._clf.predict_proba(X_cand)
+        # Predict class-membership probabilities.
+        probas = clf.predict_proba(X_cand)
 
-        # choose the method and calculate the utilities
+        # Choose the method and calculate corresponding utilities.
         with np.errstate(divide='ignore'):
             if self.method in ['least_confident', 'margin_sampling',
                                'entropy']:
-                utilities = uncertainty_scores(probas=probas,
-                                               method=self.method,
-                                               cost_matrix=self.cost_matrix)
+                utilities = uncertainty_scores(
+                    probas=probas, method=self.method,
+                    cost_matrix=self.cost_matrix
+                )
             elif self.method == 'expected_average_precision':
+                classes = clf.classes_
                 utilities = expected_average_precision(classes, probas)
             else:
                 raise ValueError(
@@ -183,12 +179,12 @@ def uncertainty_scores(probas, cost_matrix=None, method='least_confident'):
     """
     # Check probabilities.
     probas = check_array(probas, accept_sparse=False,
-            accept_large_sparse=True, dtype="numeric", order=None,
-            copy=False, force_all_finite=True, ensure_2d=True,
-            allow_nd=False, ensure_min_samples=1,
-            ensure_min_features=1, estimator=None)
+                         accept_large_sparse=True, dtype="numeric", order=None,
+                         copy=False, force_all_finite=True, ensure_2d=True,
+                         allow_nd=False, ensure_min_samples=1,
+                         ensure_min_features=1, estimator=None)
 
-    if not np.allclose(np.sum(probas, axis=1), 1, rtol=0, atol=1.e-14):
+    if not np.allclose(np.sum(probas, axis=1), 1, rtol=0, atol=1.e-3):
         raise ValueError(
             "'probas' are invalid. The sum over axis 1 must be one."
         )
@@ -225,7 +221,6 @@ def uncertainty_scores(probas, cost_matrix=None, method='least_confident'):
         )
 
 
-# expected average precision:
 def expected_average_precision(classes, probas):
     """
     Calculate the expected average precision.
@@ -241,24 +236,30 @@ def expected_average_precision(classes, probas):
     -------
     score : np.ndarray, shape=(n_X_cand)
         The expected average precision score of all instances in X_cand.
+
+    References
+    ---------
+    [1] Wang, Hanmo, et al. "Uncertainty sampling for action recognition
+        via maximizing expected average precision."
+        IJCAI International Joint Conference on Artificial Intelligence. 2018.
     """
-    # check if probas is valid
+    # Check if `probas` is valid.
     probas = check_array(probas, accept_sparse=False,
-            accept_large_sparse=True, dtype="numeric", order=None,
-            copy=False, force_all_finite=True, ensure_2d=True,
-            allow_nd=False, ensure_min_samples=1,
-            ensure_min_features=1, estimator=None)
+                         accept_large_sparse=True, dtype="numeric", order=None,
+                         copy=False, force_all_finite=True, ensure_2d=True,
+                         allow_nd=False, ensure_min_samples=1,
+                         ensure_min_features=1, estimator=None)
 
     if (np.sum(probas, axis=1) - 1).all():
         raise ValueError('probas are invalid. The sum over axis 1 must be '
                          'one.')
 
-    # check if classes is valid
+    # Check if `classes` are valid.
     check_classes(classes)
     if len(classes) < 2:
-        raise ValueError('classes must contain at least 2 entries.')
+        raise ValueError('`classes` must contain at least 2 entries.')
     if len(classes) != probas.shape[1]:
-        raise ValueError('classes must have the same length as probas has '
+        raise ValueError('`classes` must have the same length as `probas` has '
                          'columns.')
 
     score = np.zeros(len(probas))
@@ -304,5 +305,5 @@ def _f(n, t, p, f_arr, g_arr):
         return 0
     if t == 0 and n == 0:
         return 1
-    return p[n - 1] * f_arr[n - 1, t - 1] + p[n - 1] * t * g_arr[n - 1, t - 1] / n + (1 - p[n - 1]) * f_arr[n - 1, t]
-
+    return p[n - 1] * f_arr[n - 1, t - 1] + p[n - 1] * t * \
+           g_arr[n - 1, t - 1] / n + (1 - p[n - 1]) * f_arr[n - 1, t]
diff --git a/skactiveml/utils/__init__.py b/skactiveml/utils/__init__.py
index d60401df..7ff73fec 100644
--- a/skactiveml/utils/__init__.py
+++ b/skactiveml/utils/__init__.py
@@ -2,17 +2,20 @@
 The :mod:`skactiveml.utils` module includes various utilities.
 """
 from ._aggregation import compute_vote_vectors
-from ._functions import call_func, simple_batch
-from ._label import is_unlabeled, is_labeled, ExtLabelEncoder
+from ._functions import call_func, simple_batch, fit_if_not_fitted
+from ._label import is_unlabeled, is_labeled, labeled_indices, \
+    unlabeled_indices, \
+    ExtLabelEncoder
 from ._multi_annot import ext_confusion_matrix
 from ._selection import rand_argmax, rand_argmin
 from ._validation import check_classes, check_missing_label, check_scalar, \
     check_cost_matrix, check_classifier_params, check_X_y, MISSING_LABEL, \
-    check_random_state, check_class_prior
+    check_random_state, check_class_prior, check_type
 
 __all__ = ['rand_argmax', 'rand_argmin', 'compute_vote_vectors',
            'is_unlabeled', 'is_labeled', 'ExtLabelEncoder', 'check_classes',
            'check_missing_label', 'check_cost_matrix', 'check_scalar',
            'check_classifier_params', 'check_X_y', 'check_random_state',
            'MISSING_LABEL', 'call_func', 'simple_batch', 'check_class_prior',
-           'ext_confusion_matrix']
+           'ext_confusion_matrix', 'fit_if_not_fitted', 'labeled_indices',
+           'unlabeled_indices', 'check_type']
diff --git a/skactiveml/utils/_functions.py b/skactiveml/utils/_functions.py
index 65ecc022..d3a86ad8 100644
--- a/skactiveml/utils/_functions.py
+++ b/skactiveml/utils/_functions.py
@@ -2,7 +2,9 @@
 import warnings
 
 import numpy as np
-from sklearn.utils.validation import check_array
+from sklearn.base import clone
+from sklearn.utils.validation import check_array, check_is_fitted, \
+    NotFittedError
 
 from ._selection import rand_argmax
 from ._validation import check_scalar
@@ -87,3 +89,42 @@ def simple_batch(
         return best_indices, batch_utilities
     else:
         return best_indices
+
+
+def fit_if_not_fitted(estimator, X, y, sample_weight=None, print_warning=True):
+    """
+    This functions fits an estimator if it is not already fitted.
+    If the estimator is not fitted, a copy of it is created before fitting.
+
+    Parameters
+    ----------
+    estimator : skactiveml.base.SkactivemlClassifier
+        Estimator to checked regarding fitting.
+    X : matrix-like, shape (n_samples, n_features)
+        The sample matrix X is the feature matrix representing the samples.
+    y : array-like, shape (n_samples) or (n_samples, n_outputs)
+        It contains the class labels of the training samples.
+        The number of class labels may be variable for the samples, where
+        missing labels are represented the attribute 'missing_label'.
+    sample_weight : array-like, shape (n_samples) or (n_samples, n_outputs)
+        It contains the weights of the training samples' class labels.
+        It must have the same shape as y.
+    print_warning : bool, optional (default=True)
+        Flag whether waring is to printed or not.
+
+    Returns
+    -------
+    estimator : skactiveml.base.SkactivemlClassifier
+            Fitted estimator.
+    """
+    try:
+        check_is_fitted(estimator)
+        if print_warning:
+            if X is not None or y is not None or sample_weight is not None:
+                warnings.warn(
+                    'estimator is already fitted such that the parameters '
+                    '`X`, `y`, and `sample_weight` are ignored.'
+                )
+    except NotFittedError:
+        estimator = clone(estimator).fit(X, y, sample_weight)
+    return estimator
diff --git a/skactiveml/utils/_label.py b/skactiveml/utils/_label.py
index 5919ffc5..0194cdab 100644
--- a/skactiveml/utils/_label.py
+++ b/skactiveml/utils/_label.py
@@ -15,7 +15,7 @@ def is_unlabeled(y, missing_label=MISSING_LABEL):
     ----------
     y : array-like, shape (n_samples) or (n_samples, n_outputs)
         Class labels to be checked w.r.t. to missing labels.
-    missing_label : number | str | None | np.nan
+    missing_label : number | str | None | np.nan, optional (default=np.nan)
         Symbol to represent a missing label.
 
     Returns
@@ -61,7 +61,7 @@ def is_labeled(y, missing_label=MISSING_LABEL):
     ----------
     y : array-like, shape (n_samples) or (n_samples, n_outputs)
         Class labels to be checked w.r.t. to present labels.
-    missing_label : number | str | None | np.nan
+    missing_label : number | str | None | np.nan, optional (default=np.nan)
         Symbol to represent a missing label.
 
     Returns
@@ -72,6 +72,48 @@ def is_labeled(y, missing_label=MISSING_LABEL):
     return ~is_unlabeled(y, missing_label)
 
 
+def unlabeled_indices(y, missing_label=MISSING_LABEL):
+    """Return an array of indices indicating missing labels.
+
+    Parameters
+    ----------
+    y : array-like, shape (n_samples) or (n_samples, n_outputs)
+        Class labels to be checked w.r.t. to present labels.
+    missing_label : number | str | None | np.nan, optional (default=np.nan)
+        Symbol to represent a missing label.
+
+    Returns
+    -------
+    unlbld_indices : numpy.ndarray, shape (n_samples) or (n_samples, 2)
+        Index array of missing labels. If y is a 2D-array, the indices
+        have shape `(n_samples, 2), otherwise it has the shape `(n_samples)`.
+    """
+    is_unlbld = is_unlabeled(y, missing_label)
+    unlbld_indices = np.argwhere(is_unlbld)
+    return unlbld_indices[:, 0] if is_unlbld.ndim == 1 else unlbld_indices
+
+
+def labeled_indices(y, missing_label=MISSING_LABEL):
+    """Return an array of indices indicating present labels.
+
+    Parameters
+    ----------
+    y : array-like, shape (n_samples) or (n_samples, n_outputs)
+        Class labels to be checked w.r.t. to present labels.
+    missing_label : number | str | None | np.nan, optional (default=np.nan)
+        Symbol to represent a missing label.
+
+    Returns
+    -------
+    lbld_indices : numpy.ndarray, shape (n_samples) or (n_samples, 2)
+        Index array of present labels. If y is a 2D-array, the indices
+        have shape `(n_samples, 2), otherwise it has the shape `(n_samples)`.
+    """
+    is_lbld = is_labeled(y, missing_label)
+    lbld_indices = np.argwhere(is_lbld)
+    return lbld_indices[:, 0] if is_lbld.ndim == 1 else lbld_indices
+
+
 class ExtLabelEncoder(TransformerMixin, BaseEstimator):
     """Encode class labels with value between 0 and classes-1.
     This transformer should be used to encode class labels, *i.e.* `y`, and
@@ -89,7 +131,6 @@ class ExtLabelEncoder(TransformerMixin, BaseEstimator):
     classes_: array-like, shape (n_classes)
         Holds the label for each class.
     """
-
     def __init__(self, classes=None, missing_label=MISSING_LABEL):
         self.classes = classes
         self.missing_label = missing_label
diff --git a/skactiveml/utils/_validation.py b/skactiveml/utils/_validation.py
index 6b0ff6f2..48606f12 100644
--- a/skactiveml/utils/_validation.py
+++ b/skactiveml/utils/_validation.py
@@ -1,9 +1,9 @@
 import copy
+import warnings
 from collections.abc import Iterable
 
 import numpy as np
 import sklearn
-import warnings
 from sklearn.utils.validation import check_array, column_or_1d, \
     assert_all_finite, check_consistent_length
 
@@ -241,7 +241,8 @@ def check_cost_matrix(cost_matrix, n_classes, only_non_negative=False,
     return cost_matrix_new
 
 
-def check_X_y(X, y, X_cand=None, sample_weight=None, sample_weight_cand=None,
+def check_X_y(X=None, y=None, X_cand=None, sample_weight=None,
+              sample_weight_cand=None,
               accept_sparse=False, *, accept_large_sparse=True,
               dtype="numeric", order=None, copy=False, force_all_finite=True,
               ensure_2d=True, allow_nd=False, multi_output=False,
@@ -371,29 +372,35 @@ def check_X_y(X, y, X_cand=None, sample_weight=None, sample_weight_cand=None,
         The converted and validated sample_weight_cand.
         Only returned if X_cand is not None.
     """
-    if y is None:
-        raise ValueError("y cannot be None")
-
     if allow_nan is None:
         allow_nan = True if missing_label is np.nan else False
-
-    X = check_array(X, accept_sparse=accept_sparse,
-                    accept_large_sparse=accept_large_sparse,
-                    dtype=dtype, order=order, copy=copy,
-                    force_all_finite=force_all_finite,
-                    ensure_2d=ensure_2d, allow_nd=allow_nd,
-                    ensure_min_samples=ensure_min_samples,
-                    ensure_min_features=ensure_min_features,
-                    estimator=estimator)
-    if multi_output:
-        y = check_array(y, accept_sparse='csr', force_all_finite=True,
-                        ensure_2d=False, dtype=None)
-    else:
-        y = column_or_1d(y, warn=True)
-        assert_all_finite(y, allow_nan=allow_nan)
-    if y_numeric and y.dtype.kind == 'O':
-        y = y.astype(np.float64)
-    check_consistent_length(X, y)
+    if X is not None:
+        X = check_array(X, accept_sparse=accept_sparse,
+                        accept_large_sparse=accept_large_sparse,
+                        dtype=dtype, order=order, copy=copy,
+                        force_all_finite=force_all_finite,
+                        ensure_2d=ensure_2d, allow_nd=allow_nd,
+                        ensure_min_samples=ensure_min_samples,
+                        ensure_min_features=ensure_min_features,
+                        estimator=estimator)
+    if y is not None:
+        if multi_output:
+            y = check_array(y, accept_sparse='csr', force_all_finite=True,
+                            ensure_2d=False, dtype=None)
+        else:
+            y = column_or_1d(y, warn=True)
+            assert_all_finite(y, allow_nan=allow_nan)
+        if y_numeric and y.dtype.kind == 'O':
+            y = y.astype(np.float64)
+    if X is not None and y is not None:
+        check_consistent_length(X, y)
+        if sample_weight is None:
+            sample_weight = np.ones(y.shape)
+        sample_weight = check_array(sample_weight, ensure_2d=False)
+        check_consistent_length(y, sample_weight)
+        if y.ndim > 1 and y.shape[1] > 1 or \
+                sample_weight.ndim > 1 and sample_weight.shape[1] > 1:
+            check_consistent_length(y.T, sample_weight.T)
 
     if X_cand is not None:
         X_cand = check_array(X_cand, accept_sparse=accept_sparse,
@@ -404,7 +411,7 @@ def check_X_y(X, y, X_cand=None, sample_weight=None, sample_weight_cand=None,
                              ensure_min_samples=ensure_min_samples,
                              ensure_min_features=ensure_min_features,
                              estimator=estimator)
-        if X_cand.shape[1] is not X.shape[1]:
+        if X is not None and X_cand.shape[1] != X.shape[1]:
             raise ValueError("The number of features of X_cand does not match"
                              "the number of features of X")
 
@@ -413,15 +420,6 @@ def check_X_y(X, y, X_cand=None, sample_weight=None, sample_weight_cand=None,
         sample_weight_cand = check_array(sample_weight_cand, ensure_2d=False)
         check_consistent_length(X_cand, sample_weight_cand)
 
-    if sample_weight is None:
-        sample_weight = np.ones(y.shape)
-
-    sample_weight = check_array(sample_weight, ensure_2d=False)
-    check_consistent_length(y, sample_weight)
-    if y.ndim > 1 and y.shape[1] > 1 or \
-            sample_weight.ndim > 1 and sample_weight.shape[1] > 1:
-        check_consistent_length(y.T, sample_weight.T)
-
     if X_cand is None:
         return X, y, sample_weight
     else:
@@ -457,6 +455,13 @@ def check_random_state(random_state, seed_multiplier=None):
     random_state = copy.deepcopy(random_state)
     random_state = sklearn.utils.check_random_state(random_state)
 
-    seed = (random_state.randint(1, 2**31) * seed_multiplier) % (2**31)
+    seed = (random_state.randint(1, 2 ** 31) * seed_multiplier) % (2 ** 31)
     return np.random.RandomState(seed)
 
+
+def check_type(obj, target_type, name):
+    if not isinstance(obj, target_type):
+        raise TypeError(
+            f'`{name}` has type `{type(obj)}` but must have type '
+            f'`{target_type}`.'
+        )


Test patch:
diff --git a/skactiveml/classifier/tests/test_classifier.py b/skactiveml/classifier/tests/test_classifier.py
index 3e1c8621..820291ff 100644
--- a/skactiveml/classifier/tests/test_classifier.py
+++ b/skactiveml/classifier/tests/test_classifier.py
@@ -7,6 +7,7 @@
 import numpy as np
 from sklearn.datasets import make_blobs
 from sklearn.naive_bayes import GaussianNB
+from sklearn.neural_network import MLPClassifier
 from sklearn.preprocessing import StandardScaler
 from sklearn.utils.validation import NotFittedError
 
@@ -25,7 +26,7 @@ def setUp(self):
         # Create data set for testing.
         self.X, self.y_true = make_blobs(random_state=0)
         self.X = StandardScaler().fit_transform(self.X)
-        self.y = np.repeat(self.y_true.reshape(-1, 1), 2, axis=1)
+        self.y = np.repeat(self.y_true.reshape(-1, 1), 3, axis=1)
         self.y = self.y.astype('object')
         self.classes = np.unique(self.y_true)
         self.missing_label = None
@@ -36,9 +37,14 @@ def setUp(self):
         # Set up estimators for Sklearn wrapper and multi-annot classifier.
         self.estimator = GaussianNB()
         cmm = classifier.CMM(missing_label=self.missing_label, random_state=0)
-        gnb = classifier.SklearnClassifier(GaussianNB(),
-                                           missing_label=self.missing_label)
-        self.estimators = [('CMM', cmm), ('GaussianNB', gnb)]
+        gnb = classifier.SklearnClassifier(
+            GaussianNB(), missing_label=self.missing_label
+        )
+        mlp = classifier.SklearnClassifier(
+            MLPClassifier(), missing_label=self.missing_label
+        )
+        self.estimators = [('CMM', cmm), ('GaussianNB', gnb),
+                           ('MLP', mlp)]
 
     def test_classifiers(self):
         # Test predictions of classifiers.
diff --git a/skactiveml/pool/tests/test_alce.py b/skactiveml/pool/tests/test_alce.py
index 5a7a75ff..076f72f7 100644
--- a/skactiveml/pool/tests/test_alce.py
+++ b/skactiveml/pool/tests/test_alce.py
@@ -69,19 +69,7 @@ def test_init_param_nn_params(self):
         self.assertTrue(hasattr(alce, 'nn_params'))
         self.assertRaises(TypeError, alce.query, self.X_cand, self.X, self.y)
 
-    def test_init_param_random_state(self):
-        alce = ALCE(classes=self.classes, cost_matrix=self.cost_matrix,
-                    random_state='string')
-        self.assertTrue(hasattr(alce, 'random_state'))
-        self.assertRaises(ValueError, alce.query, self.X_cand, self.X, self.y)
-
     # Test query parameters
-    def test_query_param_X_cand(self):
-        alce = ALCE(self.classes, self.regressor, self.cost_matrix)
-        self.assertRaises(ValueError, alce.query, X_cand=[], X=[], y=[])
-        self.assertRaises(ValueError, alce.query, X_cand=[], X=self.X,
-                          y=self.y)
-
     def test_query_param_X(self):
         alce = ALCE(self.classes, self.regressor, self.cost_matrix)
         self.assertRaises(ValueError, alce.query, X_cand=self.X_cand,
diff --git a/skactiveml/pool/tests/test_epistemic_uncertainty.py b/skactiveml/pool/tests/test_epistemic_uncertainty.py
index e6dbfb25..924dbb00 100644
--- a/skactiveml/pool/tests/test_epistemic_uncertainty.py
+++ b/skactiveml/pool/tests/test_epistemic_uncertainty.py
@@ -27,108 +27,56 @@ def setUp(self):
         self.kwargs_MISSING_LABEL = dict(X_cand=self.X_cand, X=self.X,
                                          y=self.y_MISSING_LABEL)
 
-    def test_init_param_clf(self):
-        selector = EpistemicUncertainty(clf=PWC(),
-                                        random_state=self.random_state)
-        selector.query(**self.kwargs)
-        self.assertTrue(hasattr(selector, 'clf'))
-        # selector = QBC(clf=GaussianProcessClassifier(
-        #    random_state=self.random_state), random_state=self.random_state)
-        # selector.query(**self.kwargs)
-
-        selector = EpistemicUncertainty(clf='string')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = EpistemicUncertainty(clf=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = EpistemicUncertainty(clf=1)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = EpistemicUncertainty(
-            clf=SklearnClassifier(DecisionTreeClassifier())
-        )
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
     def test_init_param_precompute(self):
-        selector = EpistemicUncertainty(clf=self.clf, precompute=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-        selector = EpistemicUncertainty(clf=self.clf, precompute=[])
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-        selector = EpistemicUncertainty(clf=self.clf, precompute=0)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-    def test_init_param_random_state(self):
-        selector = EpistemicUncertainty(clf=self.clf, random_state='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs)
-        selector = EpistemicUncertainty(clf=self.clf,
-                                        random_state=self.random_state)
-        self.assertTrue(hasattr(selector, 'random_state'))
-        self.assertRaises(ValueError, selector.query, X_cand=[[1]], X=self.X,
-                          y=self.y)
-
-    def test_query_param_X_cand(self):
-        selector = EpistemicUncertainty(clf=self.clf)
-        self.assertRaises(ValueError, selector.query, X_cand=[], X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=None, X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=np.nan, X=self.X,
-                          y=self.y)
+        selector = EpistemicUncertainty(precompute=None)
+        self.assertRaises(TypeError, selector.query, **self.kwargs,
+                          clf=self.clf)
+
+        selector = EpistemicUncertainty(precompute=[])
+        self.assertRaises(TypeError, selector.query, **self.kwargs,
+                          clf=self.clf)
+
+        selector = EpistemicUncertainty(precompute=0)
+        self.assertRaises(TypeError, selector.query, **self.kwargs,
+                          clf=self.clf)
+
+    def test_query_param_clf(self):
+        selector = EpistemicUncertainty()
+        dt = SklearnClassifier(DecisionTreeClassifier())
+        for clf in [None, 'string', 1, dt]:
+            self.assertRaises(
+                TypeError, selector.query, **self.kwargs, clf=clf
+            )
 
     def test_query_param_X(self):
-        selector = EpistemicUncertainty(clf=self.clf)
+        selector = EpistemicUncertainty()
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=None, y=self.y)
+                          clf=self.clf, X=None, y=self.y)
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X='string', y=self.y)
+                          clf=self.clf, X='string', y=self.y)
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=[], y=self.y)
+                          clf=self.clf, X=[], y=self.y)
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X[0:-1], y=self.y)
+                          clf=self.clf, X=self.X[0:-1], y=self.y)
 
     def test_query_param_y(self):
-        selector = EpistemicUncertainty(clf=self.clf)
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=None)
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y='string')
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=[])
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y[0:-1])
+        selector = EpistemicUncertainty()
+        y_list = [None, 'string', [], self.y[0:-1]]
+        for y in y_list:
+            self.assertRaises(
+                ValueError, selector.query, X_cand=self.X_cand, clf=self.clf,
+                X=self.X, y=y
+            )
 
     def test_query_param_sample_weight(self):
-        selector = EpistemicUncertainty(clf=LogisticRegression())
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=self.X_cand)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=self.X_cand[:-2])
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=np.empty((len(self.X) - 1)))
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=np.empty((len(self.X) + 1)))
-
-    def test_query_param_batch_size(self):
-        selector = EpistemicUncertainty(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size=1.2)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=0)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=-10)
-
-    def test_query_param_return_utilities(self):
-        selector = EpistemicUncertainty(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=[])
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=0)
+        selector = EpistemicUncertainty()
+        sample_weight_list = [
+            'string', self.X_cand, self.X_cand[:-2],
+            np.empty((len(self.X) - 1)), np.empty((len(self.X) + 1))
+        ]
+        for sample_weight in sample_weight_list:
+            self.assertRaises(ValueError, selector.query, **self.kwargs,
+                              clf=self.clf, sample_weight=sample_weight)
 
     # tests for epistemic PWC
     def test_interpolate(self):
@@ -179,13 +127,15 @@ class Dummy_PWC(PWC):
             def predict_freq(self, X):
                 return freq
 
-        selector = EpistemicUncertainty(
-            clf=Dummy_PWC(classes=self.classes), precompute=True)
-        _, utilities = selector.query(**self.kwargs, return_utilities=True)
+        selector = EpistemicUncertainty(precompute=True)
+        _, utilities = selector.query(**self.kwargs,
+                                      clf=Dummy_PWC(classes=self.classes),
+                                      return_utilities=True)
         np.testing.assert_array_equal(val_utilities, utilities[0])
 
-        selector = EpistemicUncertainty(clf=PWC(classes=[0, 1, 2]))
-        self.assertRaises(ValueError, selector.query, **self.kwargs)
+        selector = EpistemicUncertainty()
+        self.assertRaises(ValueError, selector.query,
+                          clf=PWC(classes=[0, 1, 2]), **self.kwargs)
 
 
     # tests for epistemic logistic regression
@@ -249,27 +199,30 @@ def test_epistemic_uncertainty_logreg(self):
         # TODO
 
     def test_query(self):
-        selector = EpistemicUncertainty(clf=self.clf)
+        selector = EpistemicUncertainty()
 
         # return_utilities
-        L = list(selector.query(**self.kwargs, return_utilities=True))
+        L = list(selector.query(**self.kwargs, clf=self.clf,
+                                return_utilities=True))
         self.assertTrue(len(L) == 2)
-        L = list(selector.query(**self.kwargs, return_utilities=False))
+        L = list(selector.query(**self.kwargs, clf=self.clf,
+                                return_utilities=False))
         self.assertTrue(len(L) == 1)
 
         # batch_size
         bs = 3
-        selector = EpistemicUncertainty(clf=self.clf)
-        best_idx = selector.query(**self.kwargs, batch_size=bs)
+        selector = EpistemicUncertainty()
+        best_idx = selector.query(**self.kwargs, clf=self.clf,
+                                  batch_size=bs)
         self.assertEqual(bs, len(best_idx))
 
         # query - PWC
         clf = PWC(classes=self.classes, random_state=self.random_state)
-        selector = EpistemicUncertainty(clf=clf)
-        selector.query(**self.kwargs)
-        selector.query(**self.kwargs_MISSING_LABEL)
+        selector = EpistemicUncertainty()
+        selector.query(**self.kwargs, clf=clf)
+        selector.query(**self.kwargs_MISSING_LABEL, clf=clf)
 
-        best_indices, utilities = selector.query(**self.kwargs,
+        best_indices, utilities = selector.query(**self.kwargs, clf=clf,
                                                  return_utilities=True)
         self.assertEqual(utilities.shape, (1, len(self.X_cand)))
         self.assertEqual(best_indices.shape, (1,))
@@ -280,20 +233,19 @@ def test_query(self):
             random_state=self.random_state
         )
 
-        selector = EpistemicUncertainty(clf=clf)
-        selector.query(**self.kwargs)
-        selector.query(**self.kwargs_MISSING_LABEL)
+        selector = EpistemicUncertainty()
+        selector.query(**self.kwargs, clf=clf)
+        selector.query(**self.kwargs_MISSING_LABEL, clf=clf)
 
-        best_indices, utilities = selector.query(**self.kwargs,
+        best_indices, utilities = selector.query(**self.kwargs, clf=clf,
                                                  return_utilities=True)
         self.assertEqual(utilities.shape, (1, len(self.X_cand)))
         self.assertEqual(best_indices.shape, (1,))
 
         best_indices_s, utilities_s = selector.query(
-            **self.kwargs, return_utilities=True, sample_weight=[0.5, 1, 1, 1])
+            **self.kwargs, clf=clf, return_utilities=True,
+            sample_weight=[0.5, 1, 1, 1]
+        )
         comp = utilities_s == utilities
         self.assertTrue(not comp.all())
 
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/pool/tests/test_expected_error.py b/skactiveml/pool/tests/test_expected_error.py
index fab54b1e..03c58dbb 100644
--- a/skactiveml/pool/tests/test_expected_error.py
+++ b/skactiveml/pool/tests/test_expected_error.py
@@ -1,8 +1,9 @@
 import unittest
 
 import numpy as np
+from sklearn.naive_bayes import GaussianNB
 
-from skactiveml.classifier import PWC
+from skactiveml.classifier import PWC, SklearnClassifier
 from skactiveml.pool import ExpectedErrorReduction as EER
 from skactiveml.utils import MISSING_LABEL
 
@@ -16,108 +17,135 @@ def setUp(self):
         self.classes = [0, 1, 2]
         self.cost_matrix = np.eye(3)
         self.clf = PWC(classes=self.classes)
+        self.clf_partial = SklearnClassifier(
+            GaussianNB(), classes=self.classes
+        ).fit(self.X, self.y)
 
     # Test init parameters
-    def test_init_param_clf(self):
-        eer = EER(clf=3)
-        self.assertTrue(hasattr(eer, 'clf'))
-        self.assertRaises(TypeError, eer.query, self.X_cand, self.X, self.y)
-
     def test_init_param_method(self):
-        eer = EER(clf=self.clf, method='wrong_method')
+        eer = EER(method='wrong_method')
         self.assertTrue(hasattr(eer, 'method'))
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y)
+        self.assertRaises(
+            ValueError, eer.query, X_cand=self.X_cand, clf=self.clf,
+            X=self.X, y=self.y
+        )
 
         for method in ['emr', 'csl', 'log_loss']:
-            eer = EER(clf=self.clf, method=method)
+            eer = EER(method=method)
             self.assertTrue(hasattr(eer, 'method'))
             self.assertEqual(eer.method, method)
 
     def test_init_param_cost_matrix(self):
-        eer = EER(clf=self.clf,
-                  cost_matrix=np.ones((2, 3)))
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y)
-
-        eer = EER(clf=self.clf, cost_matrix='string')
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y)
-
-        eer = EER(clf=self.clf, cost_matrix=np.ones((2, 2)))
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y)
+        for cost_matrix in [np.ones((2, 3)), 'string', np.ones((2, 2))]:
+            eer = EER(cost_matrix=cost_matrix)
+            self.assertRaises(
+                ValueError, eer.query, X_cand=self.X_cand, X=self.X, y=self.y,
+                clf=self.clf
+            )
 
-    def test_init_param_random_state(self):
-        eer = EER(clf=self.clf, random_state='string')
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y)
+    def test_init_param_ignore_partial_fit(self):
+        eer = EER(ignore_partial_fit=None)
+        self.assertRaises(
+            TypeError, eer.query, X_cand=self.X_cand, X=self.X, y=self.y,
+            clf=self.clf
+        )
 
     # Test query parameters
-    def test_query_param_X_cand(self):
-        eer = EER(self.clf, cost_matrix=self.cost_matrix)
-        self.assertRaises(ValueError, eer.query, X_cand=[], X=[], y=[])
-        self.assertRaises(ValueError, eer.query, X_cand=[], X=self.X, y=self.y)
+    def test_query_param_clf(self):
+        eer = EER()
+        self.assertRaises(
+            TypeError, eer.query, X_cand=self.X_cand, X=self.X, y=self.y,
+            clf='test'
+        )
 
     def test_query_param_X(self):
-        eer = EER(self.clf, cost_matrix=self.cost_matrix)
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=np.ones((5, 3)), y=self.y)
+        eer = EER(cost_matrix=self.cost_matrix)
+        for X in [None, np.ones((5, 3))]:
+            self.assertRaises(
+                ValueError, eer.query, X_cand=self.X_cand, X=X, y=self.y,
+                clf=self.clf
+            )
+        eer = EER(cost_matrix=self.cost_matrix, method='csl')
+        self.assertRaises(
+            ValueError, eer.query, X_cand=self.X_cand, X=None, y=self.y,
+            clf=self.clf_partial
+        )
 
     def test_query_param_y(self):
-        eer = EER(self.clf, cost_matrix=self.cost_matrix)
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=self.X, y=[0, 1, 4, 0, 2, 1])
+        eer = EER(cost_matrix=self.cost_matrix)
+        y_list = [None, [0, 1, 4, 0, 2, 1]]
+        for y in y_list:
+            self.assertRaises(
+                ValueError, eer.query, X_cand=self.X_cand, X=self.X, y=y,
+                clf=self.clf
+            )
+        eer = EER(cost_matrix=self.cost_matrix, method='csl')
+        self.assertRaises(
+            ValueError, eer.query, X_cand=self.X_cand, X=self.X, y=None,
+            clf=self.clf_partial
+        )
 
     def test_query_param_sample_weight(self):
-        eer = EER(self.clf)
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight='string')
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight=np.ones(3))
+        eer = EER()
+        for sample_weight in ['string', np.ones(3)]:
+            self.assertRaises(
+                ValueError, eer.query, X_cand=self.X_cand, X=self.X, y=self.y,
+                sample_weight=sample_weight, clf=self.clf
+            )
 
     def test_query_param_sample_weight_cand(self):
-        eer = EER(self.clf)
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight_cand='string')
-        self.assertRaises(ValueError, eer.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight_cand=np.ones(3))
-
-    def test_query_param_batch_size(self):
-        eer = EER(self.clf)
-        self.assertRaises(TypeError, eer.query, self.X_cand, self.X, self.y,
-                          batch_size=1.0)
-        self.assertRaises(ValueError, eer.query, self.X_cand, self.X, self.y,
-                          batch_size=0)
-
-    def test_query_param_return_utilities(self):
-        eer = EER(self.clf, cost_matrix=self.cost_matrix)
-        self.assertRaises(TypeError, eer.query, X_cand=self.X_cand, X=self.X,
-                          y=self.y, return_utilities=None)
-        self.assertRaises(TypeError, eer.query, X_cand=self.X_cand, X=self.X,
-                          y=self.y, return_utilities=[])
-        self.assertRaises(TypeError, eer.query, X_cand=self.X_cand, X=self.X,
-                          y=self.y, return_utilities=0)
+        eer = EER()
+        for sample_weight_cand in ['string', np.ones(3)]:
+            self.assertRaises(
+                ValueError, eer.query, X_cand=self.X_cand, X=self.X, y=self.y,
+                sample_weight_cand=sample_weight_cand, clf=self.clf
+            )
 
     def test_query(self):
-        # Test methods
+        # Test methods.
         X = [[0], [1], [2]]
+        clf = PWC(classes=[0, 1])
+        clf_partial = SklearnClassifier(GaussianNB(), classes=[0, 1])
         for method in ['emr', 'csl', 'log_loss']:
-            eer = EER(clf=PWC(classes=[0, 1]), method=method)
+            eer = EER(method=method)
+            y = [MISSING_LABEL, MISSING_LABEL, MISSING_LABEL]
+            clf_partial.fit(X, y)
             _, utilities = eer.query(
-                X_cand=X, X=X, y=[MISSING_LABEL, MISSING_LABEL, MISSING_LABEL],
-                return_utilities=True
+                X_cand=X, X=X, y=y,
+                sample_weight=np.ones_like(y),
+                sample_weight_cand=np.ones_like(y),
+                clf=clf, return_utilities=True
             )
             self.assertEqual(utilities.shape, (1, len(X)))
             self.assertEqual(len(np.unique(utilities)), 1)
 
+            if method != 'csl':
+                for sample_weight_cand in [None, np.ones_like(y)]:
+                    _, utilities = eer.query(
+                        X_cand=X, clf=clf_partial,
+                        sample_weight_cand=sample_weight_cand,
+                        return_utilities=True
+                    )
+                    self.assertEqual(utilities.shape, (1, len(X)))
+                    self.assertEqual(len(np.unique(utilities)), 1)
+
             _, utilities = eer.query(
-                X_cand=X, X=X, y=[0, 1, MISSING_LABEL], return_utilities=True
+                X_cand=X, X=X, y=[0, 1, MISSING_LABEL], clf=self.clf,
+                return_utilities=True
             )
             self.assertGreater(utilities[0, 2], utilities[0, 1])
             self.assertGreater(utilities[0, 2], utilities[0, 0])
 
-        # Test scenario
+        # Test scenario.
         X_cand = [[0], [1], [2], [5]]
-        eer = EER(clf=PWC(classes=[0, 1]))
+        eer = EER()
 
-        _, utilities = eer.query(X_cand, [[1]], [0], return_utilities=True)
+        _, utilities = eer.query(
+            X_cand=X_cand, X=[[1]], y=[0], clf=PWC(classes=[0, 1]),
+            return_utilities=True
+        )
         np.testing.assert_array_equal(utilities, np.zeros((1, len(X_cand))))
-
-        query_indices = eer.query([[0], [100], [200]], [[0], [200]], [0, 1])
+        query_indices = eer.query(
+            X_cand=[[0], [100], [200]], X=[[0], [200]], y=[0, 1], clf=self.clf
+        )
         np.testing.assert_array_equal(query_indices, [1])
diff --git a/skactiveml/pool/tests/test_four_ds.py b/skactiveml/pool/tests/test_four_ds.py
index 06704f25..08554fd1 100644
--- a/skactiveml/pool/tests/test_four_ds.py
+++ b/skactiveml/pool/tests/test_four_ds.py
@@ -18,99 +18,72 @@ def setUp(self):
         self.X = StandardScaler().fit_transform(self.X)
         mixture_model = BayesianGaussianMixture(n_components=2)
         mixture_model.fit(self.X)
-        self.CMM = CMM(mixture_model=mixture_model)
-
-    def test_init_param_clf(self):
-        al4ds = FourDS(clf=None)
-        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y)
-        clf = SklearnClassifier(GaussianProcessClassifier())
-        al4ds = FourDS(clf=clf)
-        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y)
+        self.clf = CMM(mixture_model=mixture_model)
 
     def test_init_param_lmbda(self):
-        al4ds = FourDS(clf=CMM(), lmbda=True)
-        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y)
-        al4ds = FourDS(clf=CMM(), lmbda=1.1)
-        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y)
-
-    def test_init_param_random_state(self):
-        al4ds = FourDS(clf=CMM(), random_state='tests')
-        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y)
-
-    def test_query_param_batch_size(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y, batch_size=1.2)
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y, batch_size=0)
-
-    def test_query_param_return_utilities(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, X=self.X,
-                          y=self.y, return_utilities='test')
+        al4ds = FourDS(lmbda=True)
+        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=self.y)
+        al4ds = FourDS(lmbda=1.1)
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=self.y)
 
-    def test_query_param_X_cand(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(ValueError, al4ds.query, X_cand=None, X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, al4ds.query, X_cand=np.ones(5), X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, al4ds.query, X_cand=np.ones((5, 1)),
+    def test_query_param_clf(self):
+        al4ds = FourDS()
+        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, clf=None,
+                          X=self.X, y=self.y)
+        clf = SklearnClassifier(GaussianProcessClassifier())
+        al4ds = FourDS()
+        self.assertRaises(TypeError, al4ds.query, X_cand=self.X, clf=clf,
                           X=self.X, y=self.y)
 
     def test_query_param_X(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(ValueError, al4ds.query, X=None, X_cand=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, al4ds.query, X=np.ones(5), X_cand=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, al4ds.query, X=np.ones((5, 1)),
-                          X_cand=self.X, y=self.y)
+        al4ds = FourDS()
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=None, y=self.y)
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=np.ones(5), y=self.y)
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=np.ones((5, 1)), y=self.y)
 
     def test_query_param_y(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(ValueError, al4ds.query, X=self.X, X_cand=self.X,
-                          y=None)
-        self.assertRaises(ValueError, al4ds.query, X=self.X, X_cand=self.X,
-                          y=np.zeros((len(self.y), 2)))
+        al4ds = FourDS()
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=None)
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=np.zeros((len(self.y), 2)))
 
     def test_query_param_sample_weight(self):
-        al4ds = FourDS(clf=CMM())
-        self.assertRaises(ValueError, al4ds.query, X=self.X, X_cand=self.X,
-                          y=self.y, sample_weight=np.ones(1))
-        self.assertRaises(ValueError, al4ds.query, X=self.X, X_cand=self.X,
-                          y=self.y, sample_weight='test')
+        al4ds = FourDS()
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=self.y, sample_weight=np.ones(1))
+        self.assertRaises(ValueError, al4ds.query, X_cand=self.X, clf=self.clf,
+                          X=self.X, y=self.y, sample_weight='test')
 
     def test_query(self):
-        al4ds = FourDS(clf=self.CMM, random_state=self.random_state)
-        query_indices = al4ds.query(X_cand=self.X, X=self.X, y=self.y)
+        al4ds = FourDS(random_state=self.random_state)
+        query_indices = al4ds.query(X_cand=self.X, clf=self.clf, X=self.X,
+                                    y=self.y)
         self.assertEqual((1,), query_indices.shape)
-        query_indices, utilities = al4ds.query(X_cand=self.X, X=self.X,
-                                               y=self.y, return_utilities=True)
+        query_indices, utilities = al4ds.query(
+            X_cand=self.X, X=self.X, clf=self.clf, y=self.y,
+            return_utilities=True
+        )
         self.assertEqual((1,), query_indices.shape)
         self.assertEqual((1, len(self.X)), utilities.shape)
         self.assertEqual(0, np.sum(utilities < 0))
         query_indices, utilities = al4ds.query(X_cand=self.X, X=self.X,
-                                               batch_size=3, y=self.y,
-                                               return_utilities=True)
+                                               clf=self.clf, batch_size=3,
+                                               y=self.y, return_utilities=True)
         self.assertEqual((3,), query_indices.shape)
         self.assertEqual((3, len(self.X)), utilities.shape)
         self.assertEqual(3, np.sum(np.isnan(utilities)))
-        al4ds = FourDS(clf=self.CMM, random_state=self.random_state)
-        query_indices, utilities = al4ds.query(X_cand=self.X, X=self.X,
-                                               batch_size=len(self.X) + 1,
-                                               y=self.y, return_utilities=True)
+        al4ds = FourDS(random_state=self.random_state)
+        query_indices, utilities = al4ds.query(
+            X_cand=self.X, X=self.X, clf=self.clf, batch_size=len(self.X) + 1,
+            y=self.y, return_utilities=True
+        )
         self.assertEqual((len(self.X),), query_indices.shape)
         self.assertEqual((len(self.X), len(self.X)), utilities.shape)
         self.assertEqual(np.sum(np.arange(0, len(self.X))),
                          np.sum(np.isnan(utilities)))
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/pool/tests/test_pool.py b/skactiveml/pool/tests/test_pool.py
index c009801a..b997f8bb 100644
--- a/skactiveml/pool/tests/test_pool.py
+++ b/skactiveml/pool/tests/test_pool.py
@@ -6,9 +6,11 @@
 
 import numpy as np
 from sklearn.datasets import make_blobs
+from sklearn.ensemble import RandomForestClassifier
 
 from skactiveml import pool
-from skactiveml.classifier import PWC, CMM
+from skactiveml.base import SingleAnnotPoolBasedQueryStrategy
+from skactiveml.classifier import PWC, CMM, SklearnClassifier
 from skactiveml.utils import call_func, is_unlabeled, MISSING_LABEL
 
 
@@ -19,37 +21,54 @@ def setUp(self):
         self.X, self.y_true = make_blobs(n_samples=10, n_features=2, centers=2,
                                          cluster_std=1, random_state=1)
         self.budget = 5
-
+        self.clf = PWC(
+            classes=np.unique(self.y_true), missing_label=MISSING_LABEL,
+            random_state=0
+        )
+        self.cmm = CMM(
+            classes=np.unique(self.y_true), missing_label=MISSING_LABEL,
+            random_state=0
+        )
+        self.ensemble = SklearnClassifier(
+            classes=np.unique(self.y_true), missing_label=MISSING_LABEL,
+            estimator=RandomForestClassifier(random_state=0),
+            random_state=0
+        )
+
+        self.y_missing_label = np.full(self.y_true.shape, self.MISSING_LABEL)
+        self.y = self.y_true.copy()
+        self.y[:3] = self.y_true[:3]
         self.query_strategies = {}
         for qs_name in pool.__all__:
-            self.query_strategies[qs_name] = getattr(pool, qs_name)
+            qs = getattr(pool, qs_name)
+            if inspect.isclass(qs) and \
+                    issubclass(qs, SingleAnnotPoolBasedQueryStrategy):
+                self.query_strategies[qs_name] = qs
         print(self.query_strategies.keys())
 
     def test_al_cycle(self):
         for qs_name in self.query_strategies:
-            if qs_name == "FourDS":
-                clf = CMM(classes=np.unique(self.y_true),
-                          missing_label=MISSING_LABEL,
-                          random_state=np.random.RandomState(0))
-            else:
-                clf = PWC(classes=np.unique(self.y_true),
-                          missing_label=MISSING_LABEL,
-                          random_state=np.random.RandomState(0))
-
+            clf = self.cmm if qs_name == "FourDS" else self.clf
             with self.subTest(msg="Random State", qs_name=qs_name):
                 y = np.full(self.y_true.shape, self.MISSING_LABEL)
                 qs = call_func(
                     self.query_strategies[qs_name], only_mandatory=False,
-                    clf=clf, classes=np.unique(self.y_true),
-                    random_state=np.random.RandomState(0))
+                    classes=np.unique(self.y_true),
+                    random_state=np.random.RandomState(0), clf=clf,
+                    ensemble=self.ensemble
+                )
 
                 unlabeled = np.where(is_unlabeled(y))[0]
-                id1, u1 = call_func(qs.query, X_cand=self.X[unlabeled],
-                                    X=self.X, y=y, X_eval=self.X,
-                                    return_utilities=True)
-                id2, u2 = call_func(qs.query, X_cand=self.X[unlabeled],
-                                    X=self.X, y=y, X_eval=self.X,
-                                    return_utilities=True)
+                id1, u1 = call_func(
+                    qs.query, X_cand=self.X[unlabeled], X=self.X, y=y, clf=clf,
+                    X_eval=self.X, ensemble=self.ensemble,
+                    return_utilities=True
+                )
+                id2, u2 = call_func(
+                    qs.query, X_cand=self.X[unlabeled], X=self.X, y=y, clf=clf,
+                    X_eval=self.X, ensemble=self.ensemble,
+                    return_utilities=True
+                )
                 np.testing.assert_array_equal(id1, id2)
                 np.testing.assert_array_equal(u1, u2)
 
@@ -59,25 +78,34 @@ def test_al_cycle(self):
                 qs = call_func(
                     self.query_strategies[qs_name], only_mandatory=True,
                     clf=clf, classes=np.unique(self.y_true),
-                    random_state=np.random.RandomState(0))
-
-                ids, u = call_func(qs.query, X_cand=self.X[unlabeled],
-                                   X=self.X, y=y, X_eval=self.X,
-                                   batch_size=5, return_utilities=True)
+                    ensemble=self.ensemble,
+                    random_state=np.random.RandomState(0)
+                )
+
+                ids, u = call_func(
+                    qs.query, X_cand=self.X[unlabeled], X=self.X, y=y, clf=clf,
+                    X_eval=self.X, batch_size=5, ensemble=self.ensemble,
+                    return_utilities=True
+                )
                 self.assertEqual(len(ids), 5)
                 self.assertEqual(len(u), 5, msg='utility score should '
                                                 'have shape (5xN)')
                 self.assertEqual(len(u[0]), len(unlabeled),
                                  msg='utility score must have shape (5xN)')
 
-                self.assertWarns(Warning, call_func, f_callable=qs.query,
-                                 X_cand=self.X[unlabeled], X=self.X, y=y,
-                                 X_eval=self.X, batch_size=15)
+                self.assertWarns(
+                    Warning, call_func, f_callable=qs.query,
+                    X_cand=self.X[unlabeled], X=self.X, y=y, clf=clf,
+                    X_eval=self.X, ensemble=self.ensemble, batch_size=15
+                )
 
                 with warnings.catch_warnings():
                     warnings.filterwarnings("ignore")
-                    ids = call_func(qs.query, X_cand=self.X[unlabeled],
-                                    X=self.X, y=y, X_eval=self.X, batch_size=15)
+                    ids = call_func(
+                        qs.query, X_cand=self.X[unlabeled], X=self.X, y=y,
+                        X_eval=self.X, clf=clf, batch_size=15,
+                        ensemble=self.ensemble
+                    )
                     self.assertEqual(len(ids), 10)
 
             for init_budget in [5, 1, 0]:
@@ -89,52 +117,54 @@ def test_al_cycle(self):
                     qs = call_func(
                         self.query_strategies[qs_name], only_mandatory=True,
                         clf=clf, classes=np.unique(self.y_true),
-                        random_state=1)
-
-                    unlabeled = np.where(is_unlabeled(y))[0]
+                        random_state=1, ensemble=self.ensemble
+                    )
 
                     for b in range(self.budget):
                         unlabeled = np.where(is_unlabeled(y))[0]
                         clf.fit(self.X, y)
-                        ids = call_func(qs.query, X_cand=self.X[unlabeled],
-                                        X=self.X, y=y, X_eval=self.X)
+                        ids = call_func(
+                            qs.query, X_cand=self.X[unlabeled], clf=clf,
+                            X=self.X, y=y, X_eval=self.X,
+                            ensemble=self.ensemble,
+                        )
                         sample_id = unlabeled[ids]
                         y[sample_id] = self.y_true[sample_id]
 
     def test_param(self):
-        not_test = ['self', 'kwargs']
+        not_test = ['self', 'kwargs', 'random_state', 'X_cand', 'batch_size',
+                    'return_utilities']
         for qs_name in self.query_strategies:
+            clf = self.cmm if qs_name == "FourDS" else self.clf
             with self.subTest(msg="Param Test", qs_name=qs_name):
-                # get init_params
+                # Get initial parameters.
                 qs_class = self.query_strategies[qs_name]
-                init_params = list(inspect.signature(
-                    qs_class).parameters.keys())
+                init_params = inspect.signature(qs_class).parameters.keys()
+                init_params = list(init_params)
 
-                # check init params
+                # Get query parameters.
+                query_params = inspect.signature(qs_class.query).parameters
+                query_params = list(query_params.keys())
+
+                # Check initial parameters.
                 values = [Dummy() for i in range(len(init_params))]
                 qs_obj = qs_class(*values)
                 for param, value in zip(init_params, values):
                     self.assertTrue(
                         hasattr(qs_obj, param),
-                        msg='"{}" not tested for __init__()'.format(param))
+                        msg=f'"{param}" not tested for __init__()')
                     self.assertEqual(getattr(qs_obj, param), value)
 
-                # get query_params
-                query_params = list(inspect.signature(
-                    qs_class.query).parameters.keys())
-
-                # get test class to check
-                class_file_name = path.basename(inspect.getfile(qs_class))[:-3]
-                mod = import_module(
-                    'skactiveml.pool.tests.test' + class_file_name)
+                # Get class to check.
+                class_filename = path.basename(inspect.getfile(qs_class))[:-3]
+                mod = 'skactiveml.pool.tests.test' + class_filename
+                mod = import_module(mod)
                 test_class_name = 'Test' + qs_class.__name__
-                self.assertTrue(hasattr(mod, test_class_name),
-                                msg='{} has no test called {}'.format(
-                                    qs_name, test_class_name)
-                                )
+                msg = f'{qs_name} has no test called {test_class_name}.'
+                self.assertTrue(hasattr(mod, test_class_name), msg=msg)
                 test_obj = getattr(mod, test_class_name)
 
-                # check init params
+                # Check init parameters.
                 for param in np.setdiff1d(init_params, not_test):
                     test_func_name = 'test_init_param_' + param
                     self.assertTrue(
@@ -142,13 +172,57 @@ def test_param(self):
                         msg="'{}()' missing for parameter '{}' of "
                             "__init__()".format(test_func_name, param))
 
-                # check query params
+                # Check query parameters.
                 for param in np.setdiff1d(query_params, not_test):
                     test_func_name = 'test_query_param_' + param
-                    self.assertTrue(
-                        hasattr(test_obj, test_func_name),
-                        msg="'{}()' missing for parameter '{}' of "
-                            "query()".format(test_func_name, param))
+                    msg = f"'{test_func_name}()' missing for parameter " \
+                          f"'{param}' of query()"
+                    self.assertTrue(hasattr(test_obj, test_func_name), msg)
+
+                # Check standard parameters of `__init__` method.
+                self._test_init_param_random_state(qs_class, clf)
+
+                # Check standard parameters of `query` method.
+                self._test_query_param_X_cand(qs_class, clf)
+                self._test_query_param_batch_size(qs_class, clf)
+                self._test_query_param_return_utilities(qs_class, clf)
+
+    def _test_init_param_random_state(self, qs_class, clf):
+        qs_mdl = call_func(qs_class, classes=np.unique(self.y_true))
+        self.assertTrue(qs_mdl.random_state is None)
+        qs_mdl = call_func(
+            qs_class, classes=np.unique(self.y_true), clf=clf,
+            random_state='Test'
+        )
+        self.assertEqual(qs_mdl.random_state, 'Test')
+        self.assertRaises(ValueError, call_func, qs_mdl.query, X_cand=self.X,
+                          clf=clf, X=self.X, y=self.y, ensemble=self.ensemble)
+
+    def _test_query_param_X_cand(self, qs_class, clf):
+        qs_mdl = call_func(qs_class, classes=np.unique(self.y_true))
+        for X_cand in [None, [], np.ones(5)]:
+            self.assertRaises(
+                ValueError, call_func, qs_mdl.query, X_cand=X_cand, clf=clf,
+                X=self.X, y=self.y, ensemble=self.ensemble
+            )
+
+    def _test_query_param_batch_size(self, qs_class, clf):
+        qs_mdl = call_func(qs_class, classes=np.unique(self.y_true))
+        self.assertRaises(
+            ValueError, call_func, qs_mdl.query, X_cand=self.X, clf=clf,
+            X=self.X, y=self.y, batch_size=0, ensemble=self.ensemble
+        )
+        self.assertRaises(
+            TypeError, call_func, qs_mdl.query, X_cand=self.X, clf=clf,
+            X=self.X, y=self.y, batch_size=1.2, ensemble=self.ensemble
+        )
+
+    def _test_query_param_return_utilities(self, qs_class, clf):
+        qs_mdl = call_func(qs_class, classes=np.unique(self.y_true))
+        self.assertRaises(
+            TypeError, call_func, qs_mdl.query, X_cand=self.X, clf=clf,
+            X=self.X, y=self.y, return_utilities='test', ensemble=self.ensemble
+        )
 
 
 class Dummy:
diff --git a/skactiveml/pool/tests/test_probal.py b/skactiveml/pool/tests/test_probal.py
index c8feb3ce..59d3b52e 100644
--- a/skactiveml/pool/tests/test_probal.py
+++ b/skactiveml/pool/tests/test_probal.py
@@ -1,6 +1,7 @@
 import unittest
 
 import numpy as np
+from sklearn.gaussian_process import GaussianProcessClassifier
 
 from skactiveml.classifier import PWC
 from skactiveml.pool import McPAL
@@ -19,97 +20,80 @@ def setUp(self):
         self.clf = PWC(classes=self.classes)
 
     # Test init parameters
-    def test_init_param_clf(self):
-        pal = McPAL(clf=3)
-        self.assertTrue(hasattr(pal, 'clf'))
-        self.assertRaises(TypeError, pal.query, self.X_cand, self.X, self.y)
-
     def test_init_param_prior(self):
-        pal = McPAL(self.clf, prior=0)
+        pal = McPAL(prior=0)
         self.assertTrue(hasattr(pal, 'prior'))
-        self.assertRaises(ValueError, pal.query, self.X_cand, self.X, self.y)
+        self.assertRaises(ValueError, pal.query, self.X_cand, self.clf, self.X,
+                          self.y)
 
-        pal = McPAL(self.clf, prior='string')
+        pal = McPAL(self.clf)
         self.assertTrue(hasattr(pal, 'prior'))
-        self.assertRaises(TypeError, pal.query, self.X_cand, self.X, self.y)
+        self.assertRaises(TypeError, pal.query, self.X_cand, self.clf, self.X,
+                          self.y)
 
     def test_init_param_m_max(self):
-        pal = McPAL(self.clf, m_max=-2)
+        pal = McPAL(m_max=-2)
         self.assertTrue(hasattr(pal, 'm_max'))
-        self.assertRaises(ValueError, pal.query, self.X_cand, self.X, self.y)
+        self.assertRaises(ValueError, pal.query, self.X_cand, self.clf, self.X,
+                          self.y)
 
-        pal = McPAL(self.clf, m_max=1.5)
+        pal = McPAL(m_max=1.5)
         self.assertTrue(hasattr(pal, 'm_max'))
-        self.assertRaises(TypeError, pal.query, self.X_cand, self.X, self.y)
-
-    def test_init_param_random_state(self):
-        pal = McPAL(self.clf, random_state='string')
-        self.assertTrue(hasattr(pal, 'random_state'))
-        self.assertRaises(ValueError, pal.query, self.X_cand, self.X, self.y)
+        self.assertRaises(TypeError, pal.query, self.X_cand, self.clf, self.X,
+                          self.y)
 
-    # Test query parameters
-    def test_query_param_X_cand(self):
-        pal = McPAL(self.clf)
-        self.assertRaises(ValueError, pal.query, X_cand=[], X=[], y=[])
-        self.assertRaises(ValueError, pal.query, X_cand=[], X=self.X, y=self.y)
+    def test_query_param_clf(self):
+        pal = McPAL()
+        self.assertRaises(TypeError, pal.query, X_cand=self.X_cand,
+                          clf=GaussianProcessClassifier(), X=self.X, y=self.y)
 
     def test_query_param_X(self):
-        pal = McPAL(self.clf)
+        pal = McPAL()
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=np.ones((5, 3)), y=self.y)
+                          clf=self.clf, X=np.ones((5, 3)), y=self.y)
 
     def test_query_param_y(self):
-        pal = McPAL(self.clf)
+        pal = McPAL()
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=self.X, y=[0, 1, 4, 0, 2, 1])
+                          clf=self.clf, X=self.X, y=[0, 1, 4, 0, 2, 1])
 
     def test_query_param_sample_weight(self):
-        pal = McPAL(self.clf)
+        pal = McPAL()
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight='string')
+                          clf=self.clf, X=self.X, y=self.y,
+                          sample_weight='string')
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, sample_weight=np.ones(3))
+                          clf=self.clf, X=self.X, y=self.y,
+                          sample_weight=np.ones(3))
 
     def test_query_param_utility_weight(self):
-        pal = McPAL(self.clf)
+        pal = McPAL()
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, utility_weight='string')
+                          clf=self.clf, X=self.X, y=self.y,
+                          utility_weight='string')
         self.assertRaises(ValueError, pal.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y, utility_weight=np.ones(3))
-
-    def test_query_param_batch_size(self):
-        pal = McPAL(self.clf)
-        self.assertRaises(TypeError, pal.query, self.X_cand, self.X, self.y,
-                          batch_size=1.0)
-        self.assertRaises(ValueError, pal.query, self.X_cand, self.X, self.y,
-                          batch_size=0)
-
-    def test_query_param_return_utilities(self):
-        pal = McPAL(self.clf)
-        self.assertRaises(TypeError, pal.query, X_cand=self.X_cand,
-                          return_utilities=None)
-        self.assertRaises(TypeError, pal.query, X_cand=self.X_cand,
-                          return_utilities=[])
-        self.assertRaises(TypeError, pal.query, X_cand=self.X_cand,
-                          return_utilities=0)
+                          clf=self.clf, X=self.X, y=self.y,
+                          utility_weight=np.ones(3))
 
     def test_query(self):
-        mcpal = McPAL(clf=self.clf)
-        self.assertRaises(ValueError, mcpal.query, X_cand=[], X=[], y=[])
-        self.assertRaises(ValueError, mcpal.query, X_cand=[], X=self.X,
-                          y=self.y)
+        mcpal = McPAL()
+        self.assertRaises(ValueError, mcpal.query, X_cand=[], clf=self.clf,
+                          X=[], y=[])
+        self.assertRaises(ValueError, mcpal.query, X_cand=[], clf=self.clf,
+                          X=self.X, y=self.y)
         self.assertRaises(ValueError, mcpal.query, X_cand=self.X_cand,
-                          X=self.X, y=[0, 1, 4, 0, 2, 1])
+                          clf=self.clf, X=self.X, y=[0, 1, 4, 0, 2, 1])
 
         # Test missing labels
         X_cand = [[0], [1], [2], [3]]
-        mcpal = McPAL(clf=PWC(classes=[0, 1]))
-        _, utilities = mcpal.query(X_cand, [[1]], [MISSING_LABEL],
+        clf = PWC(classes=[0, 1])
+        mcpal = McPAL()
+        _, utilities = mcpal.query(X_cand, clf, [[1]], [MISSING_LABEL],
                                    return_utilities=True)
         self.assertEqual(utilities.shape, (1, len(X_cand)))
         self.assertEqual(len(np.unique(utilities)), 1)
 
-        _, utilities = mcpal.query(X_cand, X=[[0], [1], [2]],
+        _, utilities = mcpal.query(X_cand, clf, X=[[0], [1], [2]],
                                    y=[0, 1, MISSING_LABEL],
                                    return_utilities=True)
         self.assertGreater(utilities[0, 2], utilities[0, 1])
@@ -117,20 +101,16 @@ def test_query(self):
 
         # Test scenario
         X_cand = [[0], [1], [2], [5]]
-        mcpal = McPAL(clf=PWC(classes=[0, 1]))
+        mcpal = McPAL()
 
-        best_indices = mcpal.query(X_cand, X=[[1]], y=[0])
+        best_indices = mcpal.query(X_cand, clf, X=[[1]], y=[0])
         np.testing.assert_array_equal(best_indices, np.array([3]))
 
-        _, utilities = mcpal.query(X_cand, X=[[1]], y=[0],
+        _, utilities = mcpal.query(X_cand, clf, X=[[1]], y=[0],
                                    return_utilities=True)
         min_utilities = np.argmin(utilities)
         np.testing.assert_array_equal(min_utilities, np.array([1]))
 
-        best_indices = mcpal.query(X_cand=[[0], [1], [2]], X=[[0], [2]],
-                                   y=[0, 1])
+        best_indices = mcpal.query(X_cand=[[0], [1], [2]], clf=clf,
+                                   X=[[0], [2]], y=[0, 1])
         np.testing.assert_array_equal(best_indices, [1])
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/pool/tests/test_qbc.py b/skactiveml/pool/tests/test_qbc.py
index 332fcefa..4b087439 100644
--- a/skactiveml/pool/tests/test_qbc.py
+++ b/skactiveml/pool/tests/test_qbc.py
@@ -3,8 +3,7 @@
 import numpy as np
 from sklearn.ensemble import BaggingClassifier
 from sklearn.ensemble import RandomForestClassifier
-from sklearn.gaussian_process import GaussianProcessRegressor, \
-    GaussianProcessClassifier
+from sklearn.gaussian_process import GaussianProcessClassifier
 
 from skactiveml.classifier import PWC, SklearnClassifier
 from skactiveml.pool._qbc import QBC, average_kl_divergence, vote_entropy
@@ -15,188 +14,107 @@ class TestQBC(unittest.TestCase):
 
     def setUp(self):
         self.random_state = 41
-        self.X_cand = np.array([[8, 1, 6, 8], [9, 1, 6, 5], [5, 1, 6, 5]])
-        self.X = np.array(
-            [[1, 2, 5, 9], [5, 8, 4, 6], [8, 4, 5, 9], [5, 4, 8, 5]])
-        self.y = np.array([0, 0, 1, 1])
-        self.classes = np.array([0, 1])
-        self.clf = PWC(classes=self.classes, random_state=self.random_state)
-        self.kwargs = dict(X_cand=self.X_cand, X=self.X, y=self.y)
-
-    def test_init_param_clf(self):
-        selector = QBC(clf=PWC(), random_state=self.random_state)
-        selector.query(**self.kwargs)
-        self.assertTrue(hasattr(selector, 'clf'))
-
-        selector = QBC(clf=GaussianProcessClassifier())
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = QBC(clf=SklearnClassifier(GaussianProcessRegressor()))
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = QBC(clf='string')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = QBC(clf=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = QBC(clf=1)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-    def test_init_param_ensemble(self):
-        selector = QBC(clf=self.clf, ensemble=None)
-        self.assertTrue(hasattr(selector, 'ensemble'))
-
-        selector = QBC(clf=self.clf, ensemble='String')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-        selector = QBC(clf=self.clf, ensemble=PWC)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
+        self.X_cand = [[8, 1, 6, 8], [9, 1, 6, 5], [5, 1, 6, 5]]
+        self.X = [[1, 2, 5, 9], [5, 8, 4, 6], [8, 4, 5, 9], [5, 4, 8, 5]]
+        self.y = [0., 0., 1., 1.]
+        self.classes = [0, 1]
+        self.ensemble = SklearnClassifier(
+            estimator=RandomForestClassifier(random_state=0),
+            classes=self.classes, random_state=self.random_state
+        )
 
     def test_init_param_method(self):
-        selector = QBC(clf=self.clf)
+        selector = QBC()
         self.assertTrue(hasattr(selector, 'method'))
-        selector = QBC(clf=self.clf, method='String')
-        self.assertRaises(ValueError, selector.query, **self.kwargs)
-        selector = QBC(clf=self.clf, method=1)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-        selector = QBC(clf=GaussianProcessRegressor, method='KL_divergence')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-    def test_init_param_random_state(self):
-        selector = QBC(clf=self.clf, random_state='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs)
-        selector = QBC(clf=self.clf, random_state=self.random_state)
-        self.assertTrue(hasattr(selector, 'random_state'))
-        self.assertRaises(ValueError, selector.query, X_cand=[[1]], X=self.X,
-                          y=self.y)
-
-    def test_init_param_ensemble_dict(self):
-        selector = QBC(clf=self.clf, ensemble_dict='string')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = QBC(clf=self.clf, ensemble_dict=dict(kwargg='kwargg'))
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-
-    def test_query_param_X_cand(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(ValueError, selector.query, X_cand=[], X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=None, X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=np.nan, X=self.X,
-                          y=self.y)
+        for method in ['test', 0]:
+            selector = QBC(method=method)
+            self.assertRaises(
+                ValueError, selector.query, X_cand=self.X_cand, X=self.X,
+                y=self.y, ensemble=self.ensemble
+            )
+
+    def test_query_param_ensemble(self):
+        selector = QBC()
+        ensemble_list = [
+            None, 'test', 1, GaussianProcessClassifier(),
+            SklearnClassifier(GaussianProcessClassifier, classes=self.classes),
+            PWC(classes=self.classes)
+        ]
+        for ensemble in ensemble_list:
+            self.assertRaises(
+                TypeError, selector.query, X_cand=self.X_cand, X=self.X,
+                y=self.y, ensemble=ensemble
+            )
 
     def test_query_param_X(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=None, y=self.y)
-        self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X='string', y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=[], y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X[0:-1], y=self.y)
+        selector = QBC()
+        for X in [None, np.nan]:
+            self.assertRaises(
+                TypeError, selector.query, X_cand=self.X_cand, X=X, y=self.y,
+                ensemble=self.ensemble
+            )
+        for X in [[], self.X[:3]]:
+            self.assertRaises(
+                ValueError, selector.query, X_cand=self.X_cand, X=X, y=self.y,
+                ensemble=self.ensemble
+            )
 
     def test_query_param_y(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=None)
-        self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y='string')
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=[])
-        self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y[0:-1])
+        selector = QBC()
+        for y in [None, np.nan]:
+            self.assertRaises(
+                TypeError, selector.query, X_cand=self.X_cand, X=self.X, y=y,
+                ensemble=self.ensemble
+            )
+        for y in [[], self.y[:3]]:
+            self.assertRaises(
+                ValueError, selector.query, X_cand=self.X_cand, X=self.X, y=y,
+                ensemble=self.ensemble
+            )
 
     def test_query_param_sample_weight(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=self.X_cand)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=np.empty((len(self.X) - 1)))
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          sample_weight=np.empty((len(self.X) + 1)))
-
-    def test_query_param_batch_size(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size=1.2)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=0)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=-10)
-
-    def test_query_param_return_utilities(self):
-        selector = QBC(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=[])
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=0)
+        selector = QBC()
+        sample_weight_list = [
+            'test', self.X_cand, np.empty((len(self.X) - 1)),
+            np.empty((len(self.X) + 1))
+        ]
+        for sample_weight in sample_weight_list:
+            self.assertRaises(
+                ValueError, selector.query, X_cand=self.X_cand, X=self.X,
+                y=self.y, ensemble=self.ensemble, sample_weight=sample_weight
+            )
 
     def test_query(self):
-        # clf
-        selector = QBC(clf=SklearnClassifier(GaussianProcessClassifier(),
-                                             classes=self.classes))
-        selector.query(**self.kwargs)
-        selector = QBC(clf=SklearnClassifier(BaggingClassifier(),
-                                             classes=self.classes))
-        selector.query(**self.kwargs)
-
-        # ensemble
-        selector = QBC(clf=self.clf, ensemble=BaggingClassifier,
-                       ensemble_dict=dict(n_estimators=10))
-        selector.query(**self.kwargs)
-        self.assertTrue(isinstance(selector._clf.estimator, BaggingClassifier))
-        selector = QBC(clf=self.clf, ensemble=RandomForestClassifier)
-        selector.query(**self.kwargs)
-
-        clf = PWC()
-        selector = QBC(clf)
-        selector.query(**self.kwargs)
-        estimators = selector._clf.estimators_
-        self.assertTrue(type(clf) == type(estimators[0]))
-
-        # ensemble_dict
-        selector = QBC(clf=self.clf, ensemble=RandomForestClassifier,
-                       ensemble_dict=dict(n_estimators=5))
-        selector.query(**self.kwargs)
-        self.assertTrue(selector._clf.estimator.n_estimators == 5)
-
-        # return_utilities
-        L = list(selector.query(**self.kwargs, return_utilities=True))
-        self.assertTrue(len(L) == 2)
-        L = list(selector.query(**self.kwargs, return_utilities=False))
-        self.assertTrue(len(L) == 1)
-
-        # batch_size
-        bs = 3
-        selector = QBC(clf=self.clf)
-        best_idx = selector.query(**self.kwargs, batch_size=bs)
-        self.assertEqual(bs, len(best_idx))
-
-        # query
-        selector = QBC(clf=self.clf, method='vote_entropy')
-        selector.query(**self.kwargs)
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
-
-        selector = QBC(clf=self.clf, method='KL_divergence')
-        selector.query(**self.kwargs)
-
-        selector = QBC(clf=self.clf, random_state=self.random_state)
-        best_indices1, utilities1 = selector.query(**self.kwargs,
-                                                   return_utilities=True)
-        self.assertEqual(utilities1.shape, (1, len(self.X_cand)))
-        self.assertEqual(best_indices1.shape, (1,))
-        best_indices2, utilities2 = selector.query(**self.kwargs,
-                                                   return_utilities=True)
-        np.testing.assert_array_equal(utilities1, utilities2)
-        np.testing.assert_array_equal(best_indices1, best_indices2)
+        ensemble_classifiers = [
+            SklearnClassifier(classes=self.classes,
+                              estimator=GaussianProcessClassifier()
+                              ),
+            SklearnClassifier(classes=self.classes,
+                              estimator=GaussianProcessClassifier()
+                              ),
+            SklearnClassifier(classes=self.classes,
+                              estimator=GaussianProcessClassifier()
+                              ),
+        ]
+        gpc = PWC(classes=self.classes)
+        ensemble_bagging = SklearnClassifier(
+            estimator=BaggingClassifier(base_estimator=gpc),
+            classes=self.classes
+        )
+        ensemble_list = [self.ensemble, ensemble_classifiers, ensemble_bagging]
+        for ensemble in ensemble_list:
+            for method in ['KL_divergence', 'vote_entropy']:
+                selector = QBC(method=method)
+                idx, u = selector.query(
+                    X_cand=self.X_cand, ensemble=ensemble, X=self.X, y=self.y,
+                    return_utilities=True
+                )
+                self.assertEqual(len(idx), 1)
+                self.assertEqual(len(u), 1)
 
 
 class TestAverageKlDivergence(unittest.TestCase):
+
     def setUp(self):
         self.probas = np.array([[[0.3, 0.7], [0.4, 0.6]],
                                 [[0.2, 0.8], [0.5, 0.5]]])
@@ -220,11 +138,12 @@ def test_average_kl_divergence(self):
 
 
 class TestVoteEntropy(unittest.TestCase):
+
     def setUp(self):
         self.classes = np.array([0, 1, 2])
         self.votes = np.array([[0, 0, 2],
                                [1, 0, 2],
-                               [2, 1, 2]])
+                               [2, 1, 2]]).T
         self.scores = np.array([1, 0.5793801643, 0])
 
     def test_param_votes(self):
@@ -236,7 +155,6 @@ def test_param_votes(self):
                           classes=self.classes)
         self.assertRaises(ValueError, vote_entropy, votes=[[[1]]],
                           classes=self.classes)
-
         self.assertRaises(ValueError, vote_entropy, votes=np.array([[10]]),
                           classes=self.classes)
         self.assertRaises(
@@ -245,7 +163,7 @@ def test_param_votes(self):
         )
 
     def test_param_classes(self):
-        self.assertRaises(TypeError, vote_entropy, votes=self.votes,
+        self.assertRaises(ValueError, vote_entropy, votes=self.votes,
                           classes='string')
         self.assertRaises(ValueError, vote_entropy, votes=self.votes,
                           classes='class')
@@ -258,10 +176,6 @@ def test_param_classes(self):
 
     def test_vote_entropy(self):
         vote_entropy(votes=np.full((10, 10), 0), classes=self.classes)
-        vote_entropy(votes=[['s', 't']], classes='string')
         scores = vote_entropy(votes=self.votes, classes=self.classes)
         np.testing.assert_array_equal(scores.round(10), self.scores.round(10))
 
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/pool/tests/test_random.py b/skactiveml/pool/tests/test_random.py
index 9cf19cee..c1d77586 100644
--- a/skactiveml/pool/tests/test_random.py
+++ b/skactiveml/pool/tests/test_random.py
@@ -11,31 +11,6 @@ class TestRandomSampler(unittest.TestCase):
     def setUp(self):
         self.X_cand = np.zeros((100, 2))
 
-    def test_init_param_random_state(self):
-        rs = RandomSampler(random_state='string')
-        self.assertTrue(hasattr(rs, 'random_state'))
-        self.assertRaises(ValueError, rs.query, X_cand=self.X_cand)
-
-    def test_query_param_X_cand(self):
-        rand = RandomSampler()
-        self.assertRaises(ValueError, rand.query, X_cand=None)
-
-    def test_query_param_batch_size(self):
-        rand = RandomSampler()
-        self.assertRaises(TypeError, rand.query, X_cand=self.X_cand,
-                          batch_size=1.2)
-        self.assertRaises(ValueError, rand.query, X_cand=self.X_cand,
-                          batch_size=0)
-
-    def test_query_param_return_utilities(self):
-        rand = RandomSampler()
-        self.assertRaises(TypeError, rand.query, X_cand=self.X_cand,
-                          return_utilities=None)
-        self.assertRaises(TypeError, rand.query, X_cand=self.X_cand,
-                          return_utilities=[])
-        self.assertRaises(TypeError, rand.query, X_cand=self.X_cand,
-                          return_utilities=0)
-
     def test_query(self):
         rand1 = RandomSampler(random_state=RandomState(14))
         rand2 = RandomSampler(random_state=14)
diff --git a/skactiveml/pool/tests/test_uncertainty.py b/skactiveml/pool/tests/test_uncertainty.py
index aae0b471..b4d5519d 100644
--- a/skactiveml/pool/tests/test_uncertainty.py
+++ b/skactiveml/pool/tests/test_uncertainty.py
@@ -1,8 +1,7 @@
 import unittest
 
 import numpy as np
-from sklearn.gaussian_process import GaussianProcessClassifier, \
-    GaussianProcessRegressor
+from sklearn.gaussian_process import GaussianProcessClassifier
 
 from skactiveml.classifier import SklearnClassifier, PWC
 from skactiveml.pool import UncertaintySampling, expected_average_precision
@@ -19,94 +18,56 @@ def setUp(self):
         self.y = np.array([0, 0, 1, 1])
         self.classes = np.array([0, 1])
         self.clf = PWC()
-        self.kwargs = dict(X_cand=self.X_cand, X=self.X, y=self.y)
-
-    def test_init_param_clf(self):
-        selector = UncertaintySampling(clf=PWC(),
-                                       random_state=self.random_state)
-        selector.query(**self.kwargs)
-        self.assertTrue(hasattr(selector, 'clf'))
-        # selector = QBC(clf=GaussianProcessClassifier(
-        #    random_state=self.random_state), random_state=self.random_state)
-        # selector.query(**self.kwargs)
-
-        selector = UncertaintySampling(clf='string')
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(clf=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(clf=1)
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(clf=GaussianProcessClassifier())
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(
-            clf=SklearnClassifier(GaussianProcessRegressor())
-        )
-        self.assertRaises(TypeError, selector.query, **self.kwargs)
+        self.kwargs = dict(X_cand=self.X_cand, clf=self.clf,
+                           X=self.X, y=self.y)
 
     def test_init_param_method(self):
-        selector = UncertaintySampling(clf=self.clf)
+        selector = UncertaintySampling()
         self.assertTrue(hasattr(selector, 'method'))
-        selector = UncertaintySampling(clf=self.clf, method='String')
+        selector = UncertaintySampling(method='String')
         self.assertRaises(ValueError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(clf=self.clf, method=1)
+        selector = UncertaintySampling(method=1)
         self.assertRaises(TypeError, selector.query, **self.kwargs)
 
     def test_init_param_cost_matrix(self):
-        selector = UncertaintySampling(clf=self.clf,
-                                       cost_matrix=np.ones((2, 3)))
+        selector = UncertaintySampling(cost_matrix=np.ones((2, 3)))
         self.assertRaises(ValueError, selector.query, **self.kwargs)
 
-        selector = UncertaintySampling(clf=self.clf,
-                                       cost_matrix='string')
+        selector = UncertaintySampling(cost_matrix='string')
         self.assertRaises(ValueError, selector.query, **self.kwargs)
 
-        selector = UncertaintySampling(clf=self.clf,
-                                       cost_matrix=np.ones((3, 3)))
+        selector = UncertaintySampling(cost_matrix=np.ones((3, 3)))
         self.assertRaises(ValueError, selector.query, **self.kwargs)
 
-
-    def test_init_param_random_state(self):
-        selector = UncertaintySampling(clf=self.clf, random_state='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs)
-        selector = UncertaintySampling(clf=self.clf,
-                                       random_state=self.random_state)
-        self.assertTrue(hasattr(selector, 'random_state'))
-        self.assertRaises(ValueError, selector.query, X_cand=[[1]], X=self.X,
-                          y=self.y)
-
-    def test_query_param_X_cand(self):
-        selector = UncertaintySampling(clf=self.clf)
-        self.assertRaises(ValueError, selector.query, X_cand=[], X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=None, X=self.X,
-                          y=self.y)
-        self.assertRaises(ValueError, selector.query, X_cand=np.nan, X=self.X,
-                          y=self.y)
+    def test_query_param_clf(self):
+        selector = UncertaintySampling()
+        self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
+                          clf=GaussianProcessClassifier(), X=self.X, y=self.y)
 
     def test_query_param_X(self):
-        selector = UncertaintySampling(clf=self.clf)
+        selector = UncertaintySampling()
         self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=None, y=self.y)
+                          clf=self.clf, X=None, y=self.y)
         self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X='string', y=self.y)
+                          clf=self.clf, X='string', y=self.y)
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=[], y=self.y)
+                          clf=self.clf, X=[], y=self.y)
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X[0:-1], y=self.y)
+                          clf=self.clf, X=self.X[0:-1], y=self.y)
 
     def test_query_param_y(self):
-        selector = UncertaintySampling(clf=self.clf)
+        selector = UncertaintySampling()
         self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=None)
+                          clf=self.clf, X=self.X, y=None)
         self.assertRaises(TypeError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y='string')
+                          clf=self.clf, X=self.X, y='string')
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=[])
+                          clf=self.clf, X=self.X, y=[])
         self.assertRaises(ValueError, selector.query, X_cand=self.X_cand,
-                          X=self.X, y=self.y[0:-1])
+                          clf=self.clf, X=self.X, y=self.y[0:-1])
 
     def test_query_param_sample_weight(self):
-        selector = UncertaintySampling(clf=self.clf)
+        selector = UncertaintySampling()
         self.assertRaises(ValueError, selector.query, **self.kwargs,
                           sample_weight='string')
         self.assertRaises(ValueError, selector.query, **self.kwargs,
@@ -116,33 +77,13 @@ def test_query_param_sample_weight(self):
         self.assertRaises(ValueError, selector.query, **self.kwargs,
                           sample_weight=np.empty((len(self.X) + 1)))
 
-    def test_query_param_batch_size(self):
-        selector = UncertaintySampling(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size=1.2)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          batch_size='string')
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=0)
-        self.assertRaises(ValueError, selector.query, **self.kwargs,
-                          batch_size=-10)
-
-    def test_query_param_return_utilities(self):
-        selector = UncertaintySampling(clf=self.clf)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=None)
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=[])
-        self.assertRaises(TypeError, selector.query, **self.kwargs,
-                          return_utilities=0)
-
     def test_query(self):
         compare_list = []
         clf = SklearnClassifier(estimator=GaussianProcessClassifier(),
                                 random_state=self.random_state,
                                 classes=self.classes)
 
-        selector = UncertaintySampling(clf=clf)
+        selector = UncertaintySampling()
 
         # return_utilities
         L = list(selector.query(**self.kwargs, return_utilities=True))
@@ -152,37 +93,36 @@ def test_query(self):
 
         # batch_size
         bs = 3
-        selector = UncertaintySampling(clf=clf)
+        selector = UncertaintySampling()
         best_idx = selector.query(**self.kwargs, batch_size=bs)
         self.assertEqual(bs, len(best_idx))
 
         # query
-        selector = UncertaintySampling(clf=clf, method='entropy')
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector = UncertaintySampling(method='entropy')
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
         compare_list.append(selector.query(**self.kwargs))
 
-        selector = UncertaintySampling(clf=clf, method='margin_sampling')
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector = UncertaintySampling(method='margin_sampling')
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
         compare_list.append(selector.query(**self.kwargs))
 
-        selector = UncertaintySampling(clf=clf, method='least_confident')
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector = UncertaintySampling(method='least_confident')
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
         compare_list.append(selector.query(**self.kwargs))
 
-        selector = UncertaintySampling(clf=clf, method='margin_sampling',
+        selector = UncertaintySampling(method='margin_sampling',
                                        cost_matrix=[[0, 1], [1, 0]])
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
 
-        selector = UncertaintySampling(clf=clf, method='least_confident',
+        selector = UncertaintySampling(method='least_confident',
                                        cost_matrix=[[0, 1], [1, 0]])
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
 
         for x in compare_list:
             self.assertEqual(compare_list[0], x)
 
-        selector = UncertaintySampling(clf=clf,
-                                       method='expected_average_precision')
-        selector.query(X_cand=[[1]], X=[[1]], y=[MISSING_LABEL])
+        selector = UncertaintySampling(method='expected_average_precision')
+        selector.query(X_cand=[[1]], clf=clf, X=[[1]], y=[MISSING_LABEL])
         best_indices, utilities = selector.query(**self.kwargs,
                                                  return_utilities=True)
         self.assertEqual(utilities.shape, (1, len(self.X_cand)))
@@ -190,6 +130,7 @@ def test_query(self):
 
 
 class TestExpectedAveragePrecision(unittest.TestCase):
+
     def setUp(self):
         self.classes = np.array([0, 1])
         self.probas = np.array([[0.4, 0.6], [0.3, 0.7]])
@@ -266,5 +207,3 @@ def test_uncertainty_scores(self):
         scores = uncertainty_scores(self.probas, method='margin_sampling')
         np.testing.assert_allclose(val_scores, scores)
 
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/tests/__init__.py b/skactiveml/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/skactiveml/tests/test_base.py b/skactiveml/tests/test_base.py
new file mode 100644
index 00000000..c447ee8e
--- /dev/null
+++ b/skactiveml/tests/test_base.py
@@ -0,0 +1,79 @@
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from skactiveml.base import QueryStrategy, SingleAnnotPoolBasedQueryStrategy, \
+    MultiAnnotPoolBasedQueryStrategy, SkactivemlClassifier, \
+    ClassFrequencyEstimator, AnnotModelMixin
+
+
+class QueryStrategyTest(unittest.TestCase):
+
+    @patch.multiple(QueryStrategy, __abstractmethods__=set())
+    def setUp(self):
+        self.qs = QueryStrategy()
+
+    def test_fit(self):
+        self.assertRaises(NotImplementedError, self.qs.query, X_cand=None)
+
+
+class SingleAnnotPoolBasedQueryStrategyTest(unittest.TestCase):
+
+    @patch.multiple(SingleAnnotPoolBasedQueryStrategy,
+                    __abstractmethods__=set())
+    def setUp(self):
+        self.qs = SingleAnnotPoolBasedQueryStrategy()
+
+    def test_fit(self):
+        self.assertRaises(NotImplementedError, self.qs.query, X_cand=None)
+
+
+class MultiAnnotPoolBasedQueryStrategyTest(unittest.TestCase):
+
+    @patch.multiple(MultiAnnotPoolBasedQueryStrategy,
+                    __abstractmethods__=set())
+    def setUp(self):
+        self.qs = MultiAnnotPoolBasedQueryStrategy()
+
+    def test_fit(self):
+        self.assertRaises(NotImplementedError, self.qs.query, X_cand=None)
+
+
+class SkactivemlClassifierTest(unittest.TestCase):
+
+    @patch.multiple(SkactivemlClassifier, __abstractmethods__=set())
+    def setUp(self):
+        self.clf = SkactivemlClassifier(classes=[0, 1], missing_label=-1)
+
+    def test_fit(self):
+        self.assertRaises(NotImplementedError, self.clf.fit, X=None, y=None)
+
+    def test_predict_proba(self):
+        self.assertRaises(NotImplementedError, self.clf.predict_proba, X=None)
+
+    def test__validate_data(self):
+        X = np.ones((10, 2))
+        y = np.random.rand(10)
+        self.assertRaises(ValueError, self.clf._validate_data, X=X, y=y)
+
+
+class ClassFrequencyEstimatorTest(unittest.TestCase):
+
+    @patch.multiple(ClassFrequencyEstimator, __abstractmethods__=set())
+    def setUp(self):
+        self.clf = ClassFrequencyEstimator()
+
+    def test_predict_freq(self):
+        self.assertRaises(NotImplementedError, self.clf.predict_freq, X=None)
+
+
+class AnnotModelMixinTest(unittest.TestCase):
+
+    @patch.multiple(AnnotModelMixin, __abstractmethods__=set())
+    def setUp(self):
+        self.clf = AnnotModelMixin()
+
+    def test_predict_annot_proba(self):
+        self.assertRaises(NotImplementedError, self.clf.predict_annot_proba,
+                          X=None)
diff --git a/skactiveml/utils/tests/test_aggregation.py b/skactiveml/utils/tests/test_aggregation.py
index 7aa7d1f0..15d41d86 100644
--- a/skactiveml/utils/tests/test_aggregation.py
+++ b/skactiveml/utils/tests/test_aggregation.py
@@ -1,6 +1,7 @@
-import numpy as np
 import unittest
 
+import numpy as np
+
 from skactiveml.utils import compute_vote_vectors
 
 
@@ -21,7 +22,3 @@ def test_compute_vote_vectors(self):
                                      missing_label=np.nan)
         v_exp = [[0, 0, 0], [0, 0, 0]]
         np.testing.assert_array_equal(v_rec, v_exp)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/utils/tests/test_functions.py b/skactiveml/utils/tests/test_functions.py
index 6945b593..bbac33ab 100644
--- a/skactiveml/utils/tests/test_functions.py
+++ b/skactiveml/utils/tests/test_functions.py
@@ -47,7 +47,3 @@ def dummy_function(a, b=2, c=3):
         self.assertEqual(result, 50)
         result = call_func(dummy_function, only_mandatory=True, a=2, b=5, c=5)
         self.assertEqual(result, 12)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/utils/tests/test_label.py b/skactiveml/utils/tests/test_label.py
index 071c654c..5b53bfc9 100644
--- a/skactiveml/utils/tests/test_label.py
+++ b/skactiveml/utils/tests/test_label.py
@@ -1,8 +1,10 @@
-import numpy as np
 import unittest
 
+import numpy as np
 from sklearn.exceptions import NotFittedError
-from skactiveml.utils import ExtLabelEncoder, is_labeled, is_unlabeled
+
+from skactiveml.utils import ExtLabelEncoder, is_labeled, is_unlabeled, \
+    labeled_indices, unlabeled_indices
 
 
 class TestLabel(unittest.TestCase):
@@ -90,6 +92,24 @@ def test_is_labeled(self):
         np.testing.assert_array_equal(~np.array([0, 0, 0, 1], dtype=bool),
                                       is_labeled(self.y6, missing_label='nan'))
 
+    def test_unlabeled_indices(self):
+        unlbld_indices = unlabeled_indices(self.y3, missing_label=None)
+        true_unlbld_indices = [0, 4]
+        np.testing.assert_array_equal(unlbld_indices, true_unlbld_indices)
+        y = np.array([self.y3]).T
+        unlbld_indices = unlabeled_indices(y, missing_label=None)
+        true_unlbld_indices = [[0, 0], [4, 0]]
+        np.testing.assert_array_equal(unlbld_indices, true_unlbld_indices)
+
+    def test_labeled_indices(self):
+        lbld_indices = labeled_indices(self.y3, missing_label=None)
+        true_lbld_indices = [1, 2, 3]
+        np.testing.assert_array_equal(lbld_indices, true_lbld_indices)
+        y = np.array([self.y3]).T
+        lbld_indices = labeled_indices(y, missing_label=None)
+        true_lbld_indices = [[1, 0], [2, 0], [3, 0]]
+        np.testing.assert_array_equal(lbld_indices, true_lbld_indices)
+
     def test_ExtLabelEncoder(self):
         ext_le = ExtLabelEncoder(classes=[2, '2'])
         self.assertRaises(TypeError, ext_le.fit, self.y1)
@@ -136,7 +156,3 @@ def test_ExtLabelEncoder(self):
         np.testing.assert_array_equal([np.nan, 1, 2, 3, np.nan],
                                       ExtLabelEncoder(
                                           classes=cls).fit_transform(self.y1))
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/utils/tests/test_multi_annot.py b/skactiveml/utils/tests/test_multi_annot.py
index e0e0bf23..bb96f357 100644
--- a/skactiveml/utils/tests/test_multi_annot.py
+++ b/skactiveml/utils/tests/test_multi_annot.py
@@ -1,6 +1,7 @@
-import numpy as np
 import unittest
 
+import numpy as np
+
 from skactiveml.utils import ext_confusion_matrix
 
 
@@ -27,7 +28,8 @@ def test_ext_confusion_matrix(self):
                                              missing_label=None,
                                              normalize='true')
         np.testing.assert_array_equal(np.eye(3), conf_matrices[0])
-        np.testing.assert_array_equal(np.ones((3, 3)) * 1/3, conf_matrices[1])
+        np.testing.assert_array_equal(np.ones((3, 3)) * 1 / 3,
+                                      conf_matrices[1])
         conf_matrices = ext_confusion_matrix(y_true=y_true, y_pred=y_pred,
                                              missing_label=None,
                                              normalize='all')
@@ -39,7 +41,3 @@ def test_ext_confusion_matrix(self):
         np.testing.assert_array_equal(np.eye(3), conf_matrices[0])
         np.testing.assert_array_equal(np.ones((3, 3)) * 1 / 3,
                                       conf_matrices[1])
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/utils/tests/test_selection.py b/skactiveml/utils/tests/test_selection.py
index ce5bddd6..47e9a678 100644
--- a/skactiveml/utils/tests/test_selection.py
+++ b/skactiveml/utils/tests/test_selection.py
@@ -1,6 +1,7 @@
-import numpy as np
 import unittest
 
+import numpy as np
+
 from skactiveml.utils import rand_argmin, rand_argmax
 
 
@@ -47,7 +48,3 @@ def test_rand_argmax(self):
         np.testing.assert_array_equal([0, 0],
                                       rand_argmax(self.d, random_state=10))
         np.testing.assert_array_equal([1], rand_argmax(self.e))
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/skactiveml/utils/tests/test_validation.py b/skactiveml/utils/tests/test_validation.py
index 9f41911f..e1f5703f 100644
--- a/skactiveml/utils/tests/test_validation.py
+++ b/skactiveml/utils/tests/test_validation.py
@@ -73,12 +73,31 @@ def test_check_missing_label(self):
                           target_type=int)
 
     def test_check_X_y(self):
-        self.assertRaises(ValueError, check_X_y, None, None)
         X = [[1, 2], [3, 4]]
         y = [1, 0]
         X_cand = [[5, 6]]
         sample_weight = [0.4, 0.6]
-        check_X_y(X, y, X_cand, sample_weight)
+        X, y, X_cand, sample_weight, _ = check_X_y(X, y, X_cand, sample_weight)
+        self.assertTrue(isinstance(X, np.ndarray))
+        y = [[1], [0]]
+        X, y, X_cand, sample_weight, _ = check_X_y(
+            X, y, X_cand, sample_weight, multi_output=True
+        )
+        self.assertTrue(isinstance(y, np.ndarray))
+        y = np.array([1, 0], dtype=object)
+        X, y, X_cand, sample_weight, _ = check_X_y(
+            X, y, X_cand, sample_weight, y_numeric=True
+        )
+        X_cand_false = [[5]]
+        self.assertRaises(
+            ValueError, check_X_y, X, y, X_cand_false, sample_weight,
+            multi_output=True
+        )
+        y = np.array([[1, 0, 1], [2, 0, 1]])
+        self.assertRaises(
+            ValueError, check_X_y, X, y, X_cand, sample_weight,
+            multi_output=True
+        )
 
     def test_check_random_state(self):
         seed = 12
@@ -100,7 +119,3 @@ def test_check_random_state(self):
         ra = check_random_state(np.random.RandomState(None))
         rb = check_random_state(np.random.RandomState(None))
         self.assertTrue(ra.rand() != rb.rand())
-
-
-if __name__ == '__main__':
-    unittest.main()