I am writing a simple queue interface backed by a vector. For reference, the implementation looks like this:
Code
pub struct ArrayQueue<T> {
storage: Vec<Option<T>>,
head: usize,
size: usize,
}
impl<T> ArrayQueue<T> {
pub fn initialize() -> Self {
Self { storage: vec![None], head: 0, size: 0 }
}
pub fn size(&self) -> usize {
self.size
}
pub fn add(&mut self, x: T) {
if self.is_full() {
self.grow(self.storage.len());
}
let index = self.storage_index(self.size);
self.storage[index] = Some(x);
self.size += 1;
}
pub fn remove(&mut self) -> Option<T> {
if self.is_empty() {
return None;
}
let element = std::mem::take(&mut self.storage[self.head]);
self.head = self.storage_index(1);
self.size -= 1;
if self.is_too_large() {
self.shrink(self.storage.len() / 2);
}
element
}
}
I need advice about how to approach writing unit tests in the "best practices" way. I am aware of two common pieces of advice for writing tests:
- Don't test implementation details.
- Tests should fail for one and only one reason.
I often find those two pieces of advice in conflict. Let's say that I want to test the remove
method, if I want to comply with 1, I need to build first a queue with add
to start testing remove
, and possibly even use iter
to check that the queue is correct. Now my test violates 2 because I'm using add
and iter
to test remove
and they may fail for their own bugs.
A similar argument can be said about complying with 2. If I want my tests to fail for only one reason I need to build a suitable initial state for my ArrayQueue
in order to start testing remove
, so to avoid using add
I need to write implementation details in my test, like writing an initial value for storage: vec![Some(2), None, Some(0), Some(1)]
. This will force me to rewrite the tests if I change the implementation in the future.
In short, I need advice about how to approach unit testing, and in particular how to comply with 1 and 2 at the same time, if that is even possible. Needles to say, I'm by no means a seasoned software engineer. Any advice will be much appreciated.