Initial commit

This commit is contained in:
Iiro Krankka
2016-09-02 15:02:07 +03:00
parent 58919bb536
commit ce7a2d3737
53 changed files with 1534 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

41
app/build.gradle Normal file
View File

@@ -0,0 +1,41 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.codemate.booklibrary"
minSdkVersion 19
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile ('com.android.support.test.espresso:espresso-core:2.2.1') {
exclude module: 'support-annotations'
}
testCompile 'junit:junit:4.12'
testCompile 'info.cukes:cucumber-java:1.2.4'
testCompile 'info.cukes:cucumber-junit:1.2.4'
testCompile 'org.mockito:mockito-core:1.+'
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:recyclerview-v7:24.2.0'
}

17
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/ironman/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,53 @@
package com.codemate.booklibrary;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.ViewAssertion;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Created by ironman on 02/09/16.
*/
public class RecyclerViewAssertions {
public static ViewAssertion adapterItemCountEquals(int count) {
return new ItemCountAssertion(ItemCountAssertion.MODE_EQUALS, count);
}
public static ViewAssertion adapterItemCountLowerThan(int count) {
return new ItemCountAssertion(ItemCountAssertion.MODE_LESS_THAN, count);
}
private static class ItemCountAssertion implements ViewAssertion {
private static final int MODE_EQUALS = 0;
private static final int MODE_LESS_THAN = 1;
private final int mode;
private final int expectedChildCount;
ItemCountAssertion(int mode, int exptectedChildCount) {
this.mode = mode;
this.expectedChildCount = exptectedChildCount;
}
@Override
public void check (View view, NoMatchingViewException noViewFoundException) {
if (noViewFoundException != null) {
throw noViewFoundException;
}
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (mode == MODE_EQUALS) {
assertThat(expectedChildCount, is(adapter.getItemCount()));
} else if (mode == MODE_LESS_THAN) {
assertTrue(expectedChildCount > adapter.getItemCount());
}
}
}
}

View File

@@ -0,0 +1,43 @@
package com.codemate.booklibrary.ui;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.widget.EditText;
import com.codemate.booklibrary.R;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static com.codemate.booklibrary.RecyclerViewAssertions.adapterItemCountEquals;
import static com.codemate.booklibrary.RecyclerViewAssertions.adapterItemCountLowerThan;
/**
* Created by ironman on 02/09/16.
*/
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
@Test
public void allBooksDisplayedInUI() {
onView(ViewMatchers.withId(R.id.bookRecycler))
.check(adapterItemCountEquals(45));
}
@Test
public void onSearchTermEntered_NonMatchingItemsNotShown() {
onView(isAssignableFrom(EditText.class))
.perform(ViewActions.typeText("2"));
onView(withId(R.id.bookRecycler))
.check(adapterItemCountLowerThan(45));
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codemate.booklibrary">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,30 @@
package com.codemate.booklibrary.data;
import java.util.Date;
/**
* Created by ironman on 01/09/16.
*/
public class Book {
private final String title;
private final String author;
private final Date publishDate;
public Book(String title, String author, Date publishDate) {
this.title = title;
this.author = author;
this.publishDate = publishDate;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public Date getPublishDate() {
return publishDate;
}
}

View File

@@ -0,0 +1,95 @@
package com.codemate.booklibrary.data;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* Created by ironman on 01/09/16.
*/
public class Library {
private List<Book> inventory = new ArrayList<>();
public void addBook(Book book) {
inventory.add(book);
}
public void addBooks(List<Book> books) {
inventory.addAll(books);
}
public List<Book> getAllBooks() {
return inventory;
}
/**
* This is a stupid search. In a real scenario, we would have something that
* would be much better.
*/
public List<Book> search(String searchQuery) {
List<Book> results = new ArrayList<>();
if (searchQuery.matches("^[1-9][0-9]{0,3}$")) {
results.addAll(findBooksByYear(searchQuery));
}
results.addAll(findBooksByAuthor(searchQuery));
results.addAll(findBooksByTitle(searchQuery));
return results;
}
public List<Book> findBooksByAuthor(String authorQuery) {
List<Book> results = new ArrayList<>();
for (Book candidate : inventory) {
String normalizedAuthor = candidate.getAuthor().toLowerCase();
String normalizedSearchQuery = authorQuery.toLowerCase();
if (normalizedAuthor.contains(normalizedSearchQuery)) {
results.add(candidate);
}
}
return results;
}
public List<Book> findBooksByTitle(String titleQuery) {
List<Book> results = new ArrayList<>();
for (Book candidate : inventory) {
String normalizedTitle = candidate.getTitle().toLowerCase();
String normalizedSearchQuery = titleQuery.toLowerCase();
if (normalizedTitle.contains(normalizedSearchQuery)) {
results.add(candidate);
}
}
return results;
}
public List<Book> findBooksByYear(String searchedYear) {
List<Book> results = new ArrayList<>();
for (Book candidate : inventory) {
Calendar candidateCalendar = Calendar.getInstance();
candidateCalendar.setTime(candidate.getPublishDate());
int candidateYear = candidateCalendar.get(Calendar.YEAR);
if (candidateYear == Integer.valueOf(searchedYear)
|| yearsStartSimilarly(candidateYear, searchedYear)) {
results.add(candidate);
}
}
return results;
}
private boolean yearsStartSimilarly(int candidateYear, String searchedYear) {
String candidate = String.valueOf(candidateYear);
return candidate.startsWith(searchedYear);
}
}

View File

@@ -0,0 +1,78 @@
package com.codemate.booklibrary.data;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* Created by ironman on 02/09/16.
*/
public class RandomBookGenerator {
private RandomBookGenerator() {}
private static final String[] SUBJECTS = {
"Chicken", "Pig", "Hippo", "Dinosaur", "Giraffe", "Orangutan",
"Bat", "Lion", "Daddy", "Grandpa", "Donald Trump", "Coffee",
"Dog", "Rat", "Pokemon", "Cat", "Senile", "Pensioner",
"Project manager", "Salesperson"
};
private static final String[] SECOND_WORDS = {
"strip club", "that knew too little", "Strategy", "parking space",
"Office", "Banana", "Toilet", "Poop", "Fart", "Rain", "Job",
"Thing", "Radio", "Zoo", "Office", "House", "Village",
"swimming pool", "Computer"
};
private static final String[] FIRST_NAMES = {
"Bob", "Anna", "Tim", "Bethany", "Donald", "Mickey", "John",
"George", "Dick", "James", "Timothy", "Joanne", "Angus"
};
private static final String[] LAST_NAMES = {
"Doe", "Trump", "Bond", "Jackson", "Jordan", "Young", "Page",
"Johnson", "Springsteen", "Kauffman", "Schmidt", "Jokinen"
};
public static List<Book> randomBooks(int howMany) {
List<Book> books = new ArrayList<>();
for (int i = 0; i < howMany; i++) {
books.add(randomBook());
}
return books;
}
public static Book randomBook() {
String title = randomTitle();
String author = randomAuthor();
Date date = randomDate();
return new Book(title, author, date);
}
private static String randomTitle() {
String subject = RandomUtils.randomFromArray(SUBJECTS);
String secondWord = RandomUtils.randomFromArray(SECOND_WORDS);
return subject + " " + secondWord;
}
private static String randomAuthor() {
String firstName = RandomUtils.randomFromArray(FIRST_NAMES);
String lastName = RandomUtils.randomFromArray(LAST_NAMES);
return firstName + " " + lastName;
}
private static Date randomDate() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, RandomUtils.randBetween(1995, 2016));
calendar.set(Calendar.MONTH, RandomUtils.randBetween(0, 11));
calendar.set(Calendar.DAY_OF_MONTH, RandomUtils.randBetween(1, 25));
return calendar.getTime();
}
}

View File

@@ -0,0 +1,24 @@
package com.codemate.booklibrary.data;
import java.util.Random;
/**
* Created by ironman on 02/09/16.
*/
public class RandomUtils {
private RandomUtils() {}
private static final Random random;
static {
random = new Random();
}
public static <T> T randomFromArray(T[] array) {
return array[randBetween(0, array.length - 1)];
}
public static int randBetween(int start, int end) {
return start + (int) Math.round(random.nextDouble() * (end - start));
}
}

View File

@@ -0,0 +1,68 @@
package com.codemate.booklibrary.ui;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.codemate.booklibrary.data.Book;
import com.codemate.booklibrary.R;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Created by ironman on 02/09/16.
*/
public class BookAdapter extends RecyclerView.Adapter<BookAdapter.ViewHolder> {
private final SimpleDateFormat dateFormat;
private List<Book> items = new ArrayList<>();
public BookAdapter() {
dateFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.item_book, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Book book = items.get(position);
holder.title.setText(book.getTitle());
holder.author.setText(book.getAuthor());
holder.date.setText(dateFormat.format(book.getPublishDate()));
}
@Override
public int getItemCount() {
return items.size();
}
public void updateItems(List<Book> books) {
this.items = books;
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
final TextView title;
final TextView author;
final TextView date;
public ViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.bookTitle);
author = (TextView) itemView.findViewById(R.id.bookAuthor);
date = (TextView) itemView.findViewById(R.id.bookDate);
}
}
}

View File

@@ -0,0 +1,57 @@
package com.codemate.booklibrary.ui;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import com.codemate.booklibrary.data.Book;
import com.codemate.booklibrary.data.Library;
import com.codemate.booklibrary.R;
import java.util.List;
public class MainActivity extends AppCompatActivity implements MainView, SearchView.OnQueryTextListener {
private MainPresenter presenter;
private BookAdapter bookAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeViews();
presenter = new MainPresenter(this, new Library());
presenter.loadAllBooks();
}
private void initializeViews() {
bookAdapter = new BookAdapter();
RecyclerView bookRecycler = (RecyclerView) findViewById(R.id.bookRecycler);
bookRecycler.setLayoutManager(new LinearLayoutManager(this));
bookRecycler.setAdapter(bookAdapter);
SearchView searchView = (SearchView) findViewById(R.id.searchView);
searchView.setOnQueryTextListener(this);
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
presenter.searchForBooks(newText);
return false;
}
@Override
public void showBooks(List<Book> books) {
bookAdapter.updateItems(books);
}
}

View File

@@ -0,0 +1,39 @@
package com.codemate.booklibrary.ui;
import com.codemate.booklibrary.data.Book;
import com.codemate.booklibrary.data.Library;
import com.codemate.booklibrary.data.RandomBookGenerator;
import java.util.List;
/**
* Created by ironman on 02/09/16.
*/
public class MainPresenter {
private final MainView mainView;
private final Library library;
public MainPresenter(MainView mainView, Library library) {
this.mainView = mainView;
this.library = library;
}
public void searchForBooks(String searchQuery) {
List<Book> searchResults = library.search(searchQuery);
loadBooks(searchResults);
}
public void loadAllBooks() {
// Populate the library with fake dummy data. In a real app
// we would have an interactor that would fetch the books from
// a real API.
List<Book> books = RandomBookGenerator.randomBooks(45);
library.addBooks(books);
loadBooks(library.getAllBooks());
}
public void loadBooks(List<Book> books) {
mainView.showBooks(books);
}
}

View File

@@ -0,0 +1,12 @@
package com.codemate.booklibrary.ui;
import com.codemate.booklibrary.data.Book;
import java.util.List;
/**
* Created by ironman on 02/09/16.
*/
public interface MainView {
void showBooks(List<Book> books);
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.MainActivity">
<android.support.v7.widget.SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:iconifiedByDefault="false"
app:queryHint="Search by title, author or year..." />
<android.support.v7.widget.RecyclerView
android:id="@+id/bookRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:clipToPadding="false"
android:layout_below="@id/searchView"
android:scrollbars="vertical"/>
</RelativeLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/bookTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
tools:text="Book Title" />
<TextView
android:id="@+id/bookAuthor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
tools:text="Book Author" />
<TextView
android:id="@+id/bookDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="2016" />
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Book Library</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,16 @@
package com.codemate.booklibrary;
import junit.framework.TestCase;
import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
/**
* Created by ironman on 01/09/16.
*/
@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources")
public class CucumberTests extends TestCase {
}

View File

@@ -0,0 +1,20 @@
package com.codemate.booklibrary.data;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
/**
* Created by ironman on 02/09/16.
*/
public class LibraryTest {
@Test
public void addMultipleBooks_PersistsThemInLibrary() {
List<Book> books = RandomBookGenerator.randomBooks(3);
Library library = new Library();
library.addBooks(books);
Assert.assertEquals(books, library.getAllBooks());
}
}

View File

@@ -0,0 +1,27 @@
package com.codemate.booklibrary.data;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
/**
* Created by ironman on 02/09/16.
*/
public class RandomBookGeneratorTest {
@Test
public void generatingRandomBooks_ReturnsNonEmptyBookList() {
List<Book> books = RandomBookGenerator.randomBooks(15);
assertEquals(15, books.size());
for (Book book : books) {
assertNotNull(book.getTitle());
assertNotNull(book.getAuthor());
assertNotEquals(0, book.getPublishDate().getTime());
}
}
}

View File

@@ -0,0 +1,87 @@
package com.codemate.booklibrary.steps;
import com.codemate.booklibrary.data.Book;
import com.codemate.booklibrary.data.Library;
import com.codemate.booklibrary.ui.MainPresenter;
import com.codemate.booklibrary.ui.MainView;
import org.junit.Assert;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import cucumber.api.Format;
import cucumber.api.PendingException;
import cucumber.api.java.en.And;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Created by ironman on 01/09/16.
*/
public class BookSearchSteps {
private Library library = new Library();
private List<Book> results = new ArrayList<>();
/**
* Test data populations
*/
@Given(".*a book with title \"([^\"]*)\", written by \"([^\"]*)\", published in (.+)$")
public void addNewBook(String title, String author, @Format("dd MMMM yyyy") Date publishDate) throws Throwable {
Book book = new Book(title, author, publishDate);
library.addBook(book);
}
/**
* Use cases
*/
@When("^the customer wants to know all books in the library$")
public void getAllBooks() throws Throwable {
results = library.getAllBooks();
}
@When("^the customer searches for books by author \"([^\"]*)\"$")
public void searchByAuthor(String authorQuery) throws Throwable {
results = library.search(authorQuery);
}
@When("^the customer searches for books with title \"([^\"]*)\"$")
public void searchByTitle(String title) throws Throwable {
results = library.search(title);
}
@When("^the customer searches for books published in year (.*)$")
public void searchByYear(String year) throws Throwable {
results = library.search(year);
}
/**
* Assertions
*/
@Then("^(\\d+) books should be found$")
public void verifyAmountOfBooksFound(int booksFound) throws Throwable {
assertEquals(booksFound, results.size());
}
@And("^Book (\\d+) should have the title \"([^\"]*)\"$")
public void verifyBookAtPosition(int position, String title) throws Throwable {
int realPosition = position - 1;
Assert.assertEquals(title, results.get(realPosition).getTitle());
}
@And("^Books should be (.+)$")
public void verifyBookTitlesEqualTo(String titles) throws Throwable {
for (Book result : results) {
assertThat(titles, containsString(result.getTitle()));
}
}
}

View File

@@ -0,0 +1,56 @@
package com.codemate.booklibrary.ui;
import com.codemate.booklibrary.data.Book;
import com.codemate.booklibrary.data.Library;
import com.codemate.booklibrary.data.RandomBookGenerator;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Created by ironman on 02/09/16.
*/
public class MainPresenterTest {
@Mock
private MainView mainView;
@Mock
private Library library;
private MainPresenter presenter;
private List<Book> randomBooks;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
presenter = new MainPresenter(mainView, library);
randomBooks = RandomBookGenerator.randomBooks(5);
}
@Test
public void searchForBooks_ShowsThemInUI() {
when(library.search(anyString()))
.thenReturn(randomBooks);
presenter.searchForBooks("test_search");
verify(mainView).showBooks(randomBooks);
}
@Test
public void loadAllBooks_ShowsThemInUI() {
when(library.getAllBooks())
.thenReturn(randomBooks);
presenter.loadAllBooks();
verify(mainView).showBooks(randomBooks);
}
}

View File

@@ -0,0 +1,43 @@
Feature: Book Search
A customer must be able to search books by year, author or title
Background:
Given the library has a book with title "How to be awesome", written by "Iiro Krankka", published in 16 May 2016
And a book with title "My life as an awesome guy", written by "Iiro Krankka", published in 27 July 2016
And a book with title "I think my teacher is an asshat", written by "John Doe", published in 01 January 2010
Scenario: List all books
When the customer wants to know all books in the library
Then 3 books should be found
And Book 1 should have the title "How to be awesome"
And Book 2 should have the title "My life as an awesome guy"
And Book 3 should have the title "I think my teacher is an asshat"
Scenario: Search books by year
When the customer searches for books published in year 2016
Then 2 books should be found
And Book 1 should have the title "How to be awesome"
And Book 2 should have the title "My life as an awesome guy"
Scenario Outline: Search books by author
When the customer searches for books by author <author>
Then 2 books should be found
And Book 1 should have the title "How to be awesome"
And Book 2 should have the title "My life as an awesome guy"
Examples:
| author |
| "Iiro" |
| "Krankka" |
| "Iiro Krankka" |
Scenario Outline: Search books by title
When the customer searches for books with title <searched_title>
Then <search_results_count> books should be found
And Books should be <search_results>
Examples:
| searched_title | search_results_count | search_results |
| "Awesome" | 2 | "How to be awesome" and "My life as an awesome guy" |
| "asshat" | 1 | "I think my teacher is an asshat" |
| "How to be" | 1 | "How to be awesome" |